1. Prepare and explore the data
I analyze the Pennsylvania re-employment bonus experiment (Bilias,
2000; Chernozhukov, 2018).
```r
Penn <- as.data.frame(read.table(\data/penn_jae.dat\, header = TRUE))
# To study all treatment arms simultaneously [--> COMMENT]:
Penn <- subset(Penn, tg==3 | tg==0) # Keep only the 3rd treatment option
Penn$tg <- ifelse(Penn$tg == 3, 1, Penn$tg) # make it {0,3} --> {0,1}
# To study all treatment arms simultaneously [--> UNCOMMENT]:
# Penn$tg <- ifelse(Penn$tg %in% c(2, 3, 4, 5, 6), 1, Penn$tg)
attach(Penn)
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
<br>
These experiments were conducted in the 1980s by the U.S. Department of Labor to test the incentive effects of alternative compensation schemes for unemployment insurance (UI): UI claimants were randomly assigned either to a control group or one of six treatment groups.
I focus on **treatment group 3**, specifically, the one with a **high** bonus and **short** qualification:
| Group | Bonus Amount | Qualification Period | Workshop Offer |
|-----------------|--------------------------|----------------------|----------------|
| Control Group | 0 | 0 | No |
| Treatment 1 | Low (3 x WBA) | Short (6 Weeks) | Yes |
| Treatment 2 | Low (3 x WBA) | Long (12 Weeks) | Yes |
| **Treatment 3** | **High (6 x WBA)** | **Short (6 Weeks)** | **Yes** |
| Treatment 4 | High (6 x WBA) | Long (12 Weeks) | Yes |
| Treatment 5 | Initially High (6 x WBA) | Long (12 Weeks) | Yes |
| Treatment 6 | High (6 x WBA) | Long (12 Weeks) | No |
: **Table 1**: Treatment Assignments
In the control group the current rules of the UI applied. Individuals in the treatment groups were offered a cash bonus if they found a job within some pre-specified period of time (qualification period), provided that the job was retained for a specified duration.
The matrix of covariates, $X$, consists of age group dummies, gender, race, the number of dependents, quarter of the experiment, location within the state, existence of recall expectations and type of occupation:
| Variable | Description |
|--------------|--------------------------------------------------------------------------------|
| `abdt` | Chronological time of enrollment of each claimant in the experiment |
| `tg` | The treatment group (bonus amount - qualification period) of each claimant |
| `inuidur1` | A measure of the length (in weeks) of the first spell of unemployment |
| `female` | Dummy variable; it indicates if the claimant's sex is female (=1) or male (=0) |
| `black` | Dummy variable; it indicates a person of black race (=1) |
| `hispanic` | Dummy variable; it indicates a person of Hispanic race (=1) |
| `othrace` | Dummy variable; it indicates a non-white, non-black, not-Hispanic (=1) |
| `dep` | The number of dependents of each claimant |
| `q1-q6` | Six dummy variables: the quarter of enrollment |
| `recall` | 1 if answered "yes" when asked if had any expectation to be recalled |
| `agelt35` | 1 if the claimant's age is less than 35 |
| `agegt54` | 1 if the claimant's age is more than 54 |
| `durable` | 1 if the occupation of the claimant was in the sector of durable manufacturing |
| `nondurable` | 1 if the claimant worked in the sector of non-durable manufacturing |
| `lusd` | 1 if filed in areas of low unemp. rate and short duration of unemployment |
| `husd` | 1 if filed in areas of high unemp. rate and short duration of unemployment |
| `muld` | 1 if filed in areas of moderate unemp. rate and long duration of unemployment |
<br>
## 2. Descriptive statistics & simple ATE
The data comes from an experimental (RCT) setting:
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuaGVhZChQZW5uKVxuYGBgXG5gYGAifQ== -->
```r
```r
head(Penn)
<!-- rnb-source-end -->
<!-- rnb-frame-begin eyJtZXRhZGF0YSI6eyJjbGFzc2VzIjoiZGF0YS5mcmFtZSIsIm5yb3ciOjYsIm5jb2wiOjIzLCJzdW1tYXJ5Ijp7IkRlc2NyaXB0aW9uIjoiZGYgWzYgw5cgMjNdIn19LCJyZGYiOiJINHNJQUFBQUFBQUFBN1ZVelU3RE1BeDIybTVqUlV3VE8zQ0FGd0Jway9hSHVIRkJpRE1DYWRlczdicHFXVnJhRkhncW5vZkg0STQwY0x1a3k2cU5IdzBpV2Y3c3o0NGRwK250MWFodmoyd0FNTUV5RFRBckNLRnlmM2ZkdmdDd0REUUlXRkRQOURNR3RSQWNvUnlnVkFIT2JwWnkrbzd5Z3JpSitoVzV4cExmdkM3Zk51dlZ2bkNZRndZNHpqc0RPSkgrYmZ4K2lkY1hrUUpiZUgzOUZXOW9Qa1BULzFHZmZNRVRUUk9OSjZXOFhlcnYwdDl2OWlmZjhPWDhUV2ZVei8vVCttUWxhdytpd3VuY1N4QTBJWHNVUzZkRng2NlEyQkMrUkhzQlR3TTNqYnNsdXlmdDZzU2JVK2FwamNlTU9qTVZPZzJTaVBMQWtYWXRGTk9ZT2lyV2RMMUlsWHZvRnFoWG9INkJCZ1VhRnVoY05SQjdEbVZNMWFDK3gwUi9xSm0rR0tyOEdqWk94MFczTmcvNXVzZGlhZUlxUE5Yd1BHVnVhWWoxT0h6cXFFSHF0Mk5sZzhqT253VkIvc0xYcCs4d21xanBLNmZ0VWtFN2t4ajNRMnRSU3FtRmtRaENqa2xHUzFiU2swbGNjalJUbm5YbXRwMXB5bWZ0N3FERW14SDNzNkt3L3I0SXJQNUZKUDgwOGphTUQ1bGVWVFAzdUIvdzR0SVpIWHRzZGF1UEVqWndRUGw4T2xFY2NQVnAyZWhOT2lJVVZLWFlUc2lVSno4NkxENEJabFdRN3QwRkFBQT0ifQ== -->
<div data-pagedtable="false">
<script data-pagedtable-source type="application/json">
{"columns":[{"label":[""],"name":["_rn_"],"type":[""],"align":["left"]},{"label":["abdt"],"name":[1],"type":["int"],"align":["right"]},{"label":["tg"],"name":[2],"type":["dbl"],"align":["right"]},{"label":["inuidur1"],"name":[3],"type":["int"],"align":["right"]},{"label":["inuidur2"],"name":[4],"type":["int"],"align":["right"]},{"label":["female"],"name":[5],"type":["int"],"align":["right"]},{"label":["black"],"name":[6],"type":["int"],"align":["right"]},{"label":["hispanic"],"name":[7],"type":["int"],"align":["right"]},{"label":["othrace"],"name":[8],"type":["int"],"align":["right"]},{"label":["dep"],"name":[9],"type":["int"],"align":["right"]},{"label":["q1"],"name":[10],"type":["int"],"align":["right"]},{"label":["q2"],"name":[11],"type":["int"],"align":["right"]},{"label":["q3"],"name":[12],"type":["int"],"align":["right"]},{"label":["q4"],"name":[13],"type":["int"],"align":["right"]},{"label":["q5"],"name":[14],"type":["int"],"align":["right"]},{"label":["q6"],"name":[15],"type":["int"],"align":["right"]},{"label":["recall"],"name":[16],"type":["int"],"align":["right"]},{"label":["agelt35"],"name":[17],"type":["int"],"align":["right"]},{"label":["agegt54"],"name":[18],"type":["int"],"align":["right"]},{"label":["durable"],"name":[19],"type":["int"],"align":["right"]},{"label":["nondurable"],"name":[20],"type":["int"],"align":["right"]},{"label":["lusd"],"name":[21],"type":["int"],"align":["right"]},{"label":["husd"],"name":[22],"type":["int"],"align":["right"]},{"label":["muld"],"name":[23],"type":["int"],"align":["right"]}],"data":[{"1":"10824","2":"0","3":"18","4":"18","5":"0","6":"0","7":"0","8":"0","9":"2","10":"0","11":"0","12":"0","13":"0","14":"1","15":"0","16":"0","17":"0","18":"0","19":"0","20":"0","21":"0","22":"1","23":"0","_rn_":"1"},{"1":"10824","2":"0","3":"1","4":"1","5":"0","6":"0","7":"0","8":"0","9":"0","10":"0","11":"0","12":"0","13":"0","14":"1","15":"0","16":"0","17":"0","18":"0","19":"0","20":"0","21":"1","22":"0","23":"0","_rn_":"4"},{"1":"10747","2":"0","3":"27","4":"27","5":"0","6":"0","7":"0","8":"0","9":"0","10":"0","11":"0","12":"0","13":"1","14":"0","15":"0","16":"0","17":"0","18":"0","19":"0","20":"0","21":"1","22":"0","23":"0","_rn_":"5"},{"1":"10670","2":"1","3":"3","4":"3","5":"1","6":"0","7":"0","8":"0","9":"2","10":"0","11":"0","12":"1","13":"0","14":"0","15":"0","16":"0","17":"1","18":"0","19":"0","20":"0","21":"0","22":"0","23":"1","_rn_":"8"},{"1":"10768","2":"1","3":"28","4":"11","5":"1","6":"0","7":"0","8":"0","9":"0","10":"0","11":"0","12":"0","13":"1","14":"0","15":"0","16":"0","17":"0","18":"0","19":"0","20":"0","21":"0","22":"0","23":"1","_rn_":"9"},{"1":"10712","2":"1","3":"6","4":"6","5":"0","6":"0","7":"0","8":"0","9":"2","10":"0","11":"0","12":"0","13":"1","14":"0","15":"0","16":"0","17":"0","18":"0","19":"1","20":"0","21":"0","22":"0","23":"1","_rn_":"11"}],"options":{"columns":{"min":{},"max":[10],"total":[23]},"rows":{"min":[10],"max":[10],"total":[6]},"pages":{}}}
</script>
</div>
<!-- rnb-frame-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
Interestingly, there are no continuous variables apart from the response variable.
<br>
### Covariate balance check
This is done with the Eicher-Huber-White (heteroscedasticity adjusted) standard errors, including the linear terms as well as the second order interactions:
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubSA8LSBsbSh0Z34oZmVtYWxlK2JsYWNrK290aHJhY2UrZmFjdG9yKGRlcCkrcTIrcTMrcTQrcTUrcTYrYWdlbHQzNSthZ2VndDU0K2R1cmFibGUrbHVzZCtodXNkKV4yKVxubSA8LSBjb2VmdGVzdChtLCB2Y292ID0gdmNvdkhDKG0sIHR5cGU9XFxIQzFcXCkpXG5cbnN0YXJnYXplcihtWzE6MTUsXSwgdHlwZSA9IFxcdGV4dFxcLHRpdGxlID0gJ0NvdmFyaWF0ZSBiYWxhbmNlIGNoZWNrJylcbmBgYFxuYGBgIn0= -->
```r
```r
m <- lm(tg~(female+black+othrace+factor(dep)+q2+q3+q4+q5+q6+agelt35+agegt54+durable+lusd+husd)^2)
m <- coeftest(m, vcov = vcovHC(m, type=\HC1\))
stargazer(m[1:15,], type = \text\,title = 'Covariate balance check')
<!-- rnb-source-end -->
<!-- rnb-output-begin eyJkYXRhIjoiXG5Db3ZhcmlhdGUgYmFsYW5jZSBjaGVja1xuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAgICAgIEVzdGltYXRlIFN0ZC4gRXJyb3IgdCB2YWx1ZSBQcig+IHwgdHwgKVxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuKEludGVyY2VwdCkgICAwLjIwOCAgICAgMC4xNTUgICAgIDEuMzQ1ICAgICAwLjE3OSAgIFxuZmVtYWxlICAgICAgICAtMC4xMTkgICAgMC4xMzYgICAgLTAuODc2ICAgICAwLjM4MSAgIFxuYmxhY2sgICAgICAgICAwLjE2MyAgICAgMC4wNzkgICAgIDIuMDU4ICAgICAwLjA0MCAgIFxub3RocmFjZSAgICAgICAwLjA0NCAgICAgMC4yODcgICAgIDAuMTUyICAgICAwLjg3OSAgIFxuZmFjdG9yKGRlcCkxICAwLjQzMCAgICAgMC4xNzUgICAgIDIuNDU0ICAgICAwLjAxNCAgIFxuZmFjdG9yKGRlcCkyICAwLjEzMCAgICAgMC4xNTIgICAgIDAuODUzICAgICAwLjM5NCAgIFxucTIgICAgICAgICAgICAwLjE0MSAgICAgMC4xNTYgICAgIDAuOTAzICAgICAwLjM2NyAgIFxucTMgICAgICAgICAgICAwLjExNSAgICAgMC4xNTYgICAgIDAuNzM5ICAgICAwLjQ2MCAgIFxucTQgICAgICAgICAgICAwLjEzMyAgICAgMC4xNTYgICAgIDAuODU2ICAgICAwLjM5MiAgIFxucTUgICAgICAgICAgICAwLjEwNiAgICAgMC4xNTUgICAgIDAuNjgzICAgICAwLjQ5NCAgIFxucTYgICAgICAgICAgICAwLjAzMiAgICAgMC4xNTQgICAgIDAuMjA5ICAgICAwLjgzNCAgIFxuYWdlbHQzNSAgICAgICAwLjEwMyAgICAgMC4xMzAgICAgIDAuNzkzICAgICAwLjQyOCAgIFxuYWdlZ3Q1NCAgICAgICAwLjM0NiAgICAgMC4yMjMgICAgIDEuNTUyICAgICAwLjEyMSAgIFxuZHVyYWJsZSAgICAgICAtMC4xMzIgICAgMC4xNjcgICAgLTAuNzkwICAgICAwLjQzMCAgIFxubHVzZCAgICAgICAgICAwLjE0MCAgICAgMC4wNjggICAgIDIuMDY1ICAgICAwLjAzOSAgIFxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIn0= -->
Covariate balance check
Estimate Std. Error t value Pr(> | t| )
(Intercept) 0.208 0.155 1.345 0.179
female -0.119 0.136 -0.876 0.381
black 0.163 0.079 2.058 0.040
othrace 0.044 0.287 0.152 0.879
factor(dep)1 0.430 0.175 2.454 0.014
factor(dep)2 0.130 0.152 0.853 0.394
q2 0.141 0.156 0.903 0.367
q3 0.115 0.156 0.739 0.460
q4 0.133 0.156 0.856 0.392
q5 0.106 0.155 0.683 0.494
q6 0.032 0.154 0.209 0.834
agelt35 0.103 0.130 0.793 0.428
agegt54 0.346 0.223 1.552 0.121
durable -0.132 0.167 -0.790 0.430
lusd 0.140 0.068 2.065 0.039
—————————————————-
<!-- rnb-output-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
Only a portion is printed for brevity. Generally, the covariates are balanced, with very few exceptions. According to the F-test, the covariates are not jointly significant at all conventional significance levels:
F-statistic: 0.9825 on 102 and 5136 DF, p-value: 0.5315
I assume that the RCT assumptions hold.
<br>
### Simple ATE estimation
To evaluate the impact of the treatment on unemployment duration, I consider the linear regression model:
$$ Y = W\beta_1 + \mathbf{X}'\beta_2 + \epsilon, \quad E(\epsilon | W, \mathbf{X}')' = 0, $$
where $Y$ is the **log** of the duration of unemployment, $W$ is a treatment indicator, and $\mathbf{X}$ is a set of controls defined previously. Here $\beta_1$ is the ATE, if the RCT assumptions hold.
I also consider an interactive regression model:
$$ Y = W\alpha_1 + W \mathbf{X}'\alpha_2 + \mathbf{X}'\beta_2 + \epsilon, \quad E(\epsilon | W, \mathbf{X}', W\mathbf{X}')' = 0, $$
where $\mathbf{X}'$'s are demeaned (apart from the intercept), so that $\alpha_1$ is the ATE, if the RCT assumptions hold.
Under RCT, the projection coefficient $\beta_1$ has the interpretation of the causal effect of the treatment on the average outcome (ATE). The covariates here are independent of the treatment $W$, so we can identify $\beta_1$ by just linear regression of $Y$ on $W$, without adding covariates. However, I do add covariates to improve the precision of the ATE estimates.
I specify four models:
1. Simple treated-control mean comparison,
2. Adding covariates as controls,
3. Running an interactive regression model (*including all variables and second-order interactions*),
4. Partialling out with Lasso.
Last two methods are taken from Viktor Chernozukov's [kaggle](https://www.kaggle.com/code/victorchernozhukov/analyzing-rct-reemployment-experiment/notebook).
The omitted dummies, ensuring no perfect multicollinearity, are: `q1`, `nondurable`, `muld`.
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuIyAxXSBOYWl2ZSBtZWFuIGNvbXBhcmlzb24gXG5vbHMuc2ltcGxlICA9ICBsbShsb2coaW51aWR1cjEpfnRnKVxub2xzLnNpbXBsZSAgPSAgY29lZnRlc3Qob2xzLnNpbXBsZSwgdmNvdiA9IHZjb3ZIQyhvbHMuc2ltcGxlLCB0eXBlPVwiSEMxXCIpKVxuXG4jIDJdIFJlZ3Jlc3Npb24gd2l0aCBjb250cm9sc1xub2xzLmNvbnRyb2wgPSBsbShsb2coaW51aWR1cjEpfnRnKyAoZmVtYWxlK2JsYWNrK290aHJhY2UrZmFjdG9yKGRlcCkrcTIrcTMrcTQrcTUrcTYrYWdlbHQzNSthZ2VndDU0K2R1cmFibGUrbHVzZCtodXNkKV4yKVxub2xzLmNvbnRyb2wgPSBjb2VmdGVzdChvbHMuY29udHJvbCwgdmNvdiA9IHZjb3ZIQyhvbHMuY29udHJvbCwgdHlwZT1cIkhDMVwiKSlcblxueCA9IG1vZGVsLm1hdHJpeCAofiAoZmVtYWxlK2JsYWNrK290aHJhY2UrZmFjdG9yKGRlcCkrcTIrcTMrcTQrcTUrcTYrYWdlbHQzNSthZ2VndDU0K2R1cmFibGUrbHVzZCtodXNkKV4yKVssLTFdXG54ID0gc2NhbGUoeCwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSlcblxuIyAzXSBQYXJ0aWFsbGluZyBvdXQgd2l0aCBsYXNzb1xudGdfZGVtZWFuICA9IHRnIC0gbWVhbih0ZylcbkR4ICAgICAgICAgPSBtb2RlbC5tYXRyaXgofnRnX2RlbWVhbip4KVssLTFdXG5ybGFzc28uaXJhID0gc3VtbWFyeShybGFzc29FZmZlY3RzKER4LCBsb2coaW51aWR1cjEpLCBpbmRleCA9IDEpKVxuXG4jIDRdIEludGVyYWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWwgXG5vbHMuaXJhID0gbG0obG9nKGludWlkdXIxKSB+IHRnKngpIFxub2xzLmlyYSA9IGNvZWZ0ZXN0KG9scy5pcmEsIHZjb3YgPSB2Y292SEMob2xzLmlyYSwgdHlwZT1cIkhDMVwiKSlcblxub2xzLmlyYV9vcmQ8LSBvbHMuaXJhW29yZGVyKG9scy5pcmFbLCBcIlByKD58dHwpXCJdKSwgXVxuc3RhcmdhemVyKG9scy5pcmFfb3JkWzE6MTUsXSwgdHlwZSA9IFwidGV4dFwiLHRpdGxlID0gJ0ludGVyYWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWwnKVxuYGBgIn0= -->
```r
# 1] Naive mean comparison
ols.simple = lm(log(inuidur1)~tg)
ols.simple = coeftest(ols.simple, vcov = vcovHC(ols.simple, type="HC1"))
# 2] Regression with controls
ols.control = lm(log(inuidur1)~tg+ (female+black+othrace+factor(dep)+q2+q3+q4+q5+q6+agelt35+agegt54+durable+lusd+husd)^2)
ols.control = coeftest(ols.control, vcov = vcovHC(ols.control, type="HC1"))
x = model.matrix (~ (female+black+othrace+factor(dep)+q2+q3+q4+q5+q6+agelt35+agegt54+durable+lusd+husd)^2)[,-1]
x = scale(x, center = TRUE, scale = FALSE)
# 3] Partialling out with lasso
tg_demean = tg - mean(tg)
Dx = model.matrix(~tg_demean*x)[,-1]
rlasso.ira = summary(rlassoEffects(Dx, log(inuidur1), index = 1))
# 4] Interactive regression model
ols.ira = lm(log(inuidur1) ~ tg*x)
ols.ira = coeftest(ols.ira, vcov = vcovHC(ols.ira, type="HC1"))
ols.ira_ord<- ols.ira[order(ols.ira[, "Pr(>|t|)"]), ]
stargazer(ols.ira_ord[1:15,], type = "text",title = 'Interactive regression model')
Sorted by p-value. Only a part is printed for
brevity. Based on the IRA results one may conclude that there is a
possible treatment effect heterogeneity: some interactions between \(X\) and \(W\) are significant.
OLS_results(ols.simple, ols.control, ols.ira, rlasso.ira)
Treatment group №3 experiences an average decrease of about
5.8% in the length of unemployment spell, albeit the
effects are not significant at the 5% level.
We also see the regression estimators offer slightly lower estimates:
the difference is perhaps due to minor imbalance in the treatment
allocation, which the regression estimators try to correct.
Study’s report
conclusion:
Estimates based on data from UI wage records fail to provide any
evidence that the bonus offers enhanced the employment of claimants
following the benefit application date.
3. Modeling choices and heterogeneity detection
The choice of propensity score
Firstly, I define the standard variables necessary for future
estimation (\(Y,X,W\)):
# Produce matrix of covariates X, treatment indicator W and outcome vector Y:
X <- model.matrix(~ (female+black+othrace+factor(dep)+q2+q3+q4+q5+q6+agelt35+agegt54+durable+lusd+husd))[,-1]
W <- tg
Y <- log(inuidur1)
In the third treatment arm, there are 1,885 treated units and 3,354
units in the control group:
table(tg)
As the treatment is randomly assigned, I use the fraction of treated
as the estimator of the propensity score (follows Chernozhukov et
al.,2018). Hence, the propensity score is constant: this value is used
by learners for the estimation of CATE.
# e_rct = rep(e_rct, length(Y))
e_rct = mean(W)
e_rct
To double check: propensity scores estimated by regression forest are
tightly clustered around mean(W).
# Propensity model
W_hat <- regression_forest(X, W, tune.parameters = "all")
W_hat.rf <- W_hat$predictions
hist(W_hat.rf, main = "Estimated propensity scores", xlab = "Propensity Score", xlim = c(0, 1))
Heterogeneity detection
I begin by testing pre-specified null hypotheses of the form
\[
H_{0}: \mathop{\mathrm{E}}[Y(1) - Y(0) | G_i = 1] =
\mathop{\mathrm{E}}[Y(1) - Y(0) | G_i = 0].
\]
That is, that the treatment effect is the same regardless of
membership to the group \(G_i\). I
choose to examine how the outcome of participating in the experiment
differs for younger people (agelt35 = 1).
When predicting the conditional average treatment effect \(\tau(X_i)\) for observation \(i\) we should in general avoid using a
model that was fitted using observation \(i\) This sort of sample splitting
(honesty) is one of the required ingredients to get
unbiased estimates of the CATE. I do 3 fold cross-fitting:
Y_tilde = aipw_GATE(X,W,Y,e_rct,3)
summary(lm_robust(Y_tilde ~ X[, 'agelt35']))
We observe that younger people may experience approximately a
14% reduction in the duration of unemployment spells
compared to the average program effect. However, this result is not
robust across different seeds and numbers of folds.
With no further hypotheses to explore, I provide a summary of the
underlying effect heterogeneity:
summary(lm_robust(Y_tilde ~ X))
The coefficients in this regression suggest general trends, but they
should not be interpreted as partial effects. Such interpretation would
only be accurate if the true model were genuinely linear in covariates —
an assumption we generally shouldn’t be willing to make.
While some coefficients in the interactive regression may appear
significant, they could be false positives. A more aggregated AIPW
regression indicates no linear effect heterogeneity.
4. Split the data into training and test sample
set.seed(123)
indices <- sample(1:nrow(X), size = 0.5*nrow(X))
train_idx <- numeric(nrow(Penn))
train_idx[indices] <- 1
X_train = X[indices,]
X_test = X[-indices,]
Y_train = Y[indices]
Y_test = Y[-indices]
W_train = W[indices]
W_test = W[-indices]
# Summary of treated and control in train and test samples:
table(Train = ifelse(Penn[train_idx==1, ]$tg == 0, "Control", "Treated"))
table(Test = ifelse(Penn[train_idx==0, ]$tg == 0, "Control", "Treated"))
5. Apply Causal ML methods
Before applying any machine learning methods, I think it is important
to discuss the estimation methods. The following reasoning is based on
Okasa (2022).
Their simulation evidence suggests that using the full sample for
estimation of both the nuisance functions as well as the CATE function
leads to a remarkably good performance in terms of both bias and
variance in finite samples.
The possible reason for this phenomenon might be due to the
out-of-bag predictions of the forest that are used throughout the
simulations. Even though these predictions are not out-of-sample per
se they are not directly based on the observations used for
estimation and as such might help to alleviate the overfitting problem
when using full sample.
Using cross-fitted estimation, one should (theoretically) observe a
smaller bias but higher variance of the estimators. However, in almost
all cases considered in the paper the authors observe both higher bias
as well as higher variance, particularly for the small sample sizes.
Based on the above simulation evidence, it seems reasonable to always
use the full-sample estimation together with out-of-bag
predictions when a relatively small sample is available,
whereas to use the cross-fitting procedure when a relatively large data
is accessible, regardless of the choice of a
meta-learner.
Given that the total number of observations in the Penn
dataset is limited (\(n=5239\)), I
prefer to do the full-sample estimation.
S-learner
Use ML estimator to fit model \(\hat{m}(W,X)\) in the whole training
sample
Estimate CATE as \(\hat{\tau}(x) =
\hat{m}(1,x) - \hat{m}(0,x)\)
If the treatment indicator is not strongly predictive for the
outcome, the S-learner will tend to estimate a zero treatment
effect.
To verify \(W\)’s low predictive
power, I first estimate whether post-lasso chooses the treatment
variable \(W\) when predicting \(Y\):
post_lasso = hdm::rlasso(x = cbind(W,X),y = Y)
post_lasso$coefficients[post_lasso$coefficients != 0]
The treatment \(W\) is not selected.
I then do the similar operation but using a decision tree:
fit <- rpart(Y~ W+X,control = rpart.control(cp = 0.0015))
rpart.plot(fit)
For this dataset, I conclude that the treatment \(W\) is not a strong predictor for the
outcome \(Y\), which could potentially
undermine the performance of the S-learner. Moreover, based on the
contemporary literature:
- Okasa (2022) does not recommend the usage of the S-learner for
estimation of heterogeneous causal effects due to empirically documented
undesirable statistical properties such as high bias and consistency
issues.
- Alaa and van der Schaar (2018) similarly find the T-learner to
uniformly outperform the S-learner in estimating heterogeneous treatment
effects.
- Related simulation studies (Jacob, 2020; Knaus et al., 2021) find
also a relatively competitive performance of the T-learner, especially
with the Random Forest as a base learner.
Hence, I have decided to exclude this method due to unfavorable
estimation conditions.
T-Learner
For the T-learner, I implement the following procedure:
Use regression forest to fit model \(\hat{m}(1,X)\) in treated
Use regression forest to fit model \(\hat{m}(0,X)\) in controls
Estimate CATE as \(\hat{\tau}(x) =
\hat{m}(1,x) - \hat{m}(0,x)\)
This procedure is expected to work particularly well if the CATE
function is complicated and there are no common trends in the response
functions. It is expected to work rather poorly if the CATE function is
simple, as the response functions are not trained jointly and their
difference is unstable (Okasa, 2022).
No additional sample-splitting induced by multiple nuisance functions
is required as the response functions are themselves estimated on
separate samples defined by treated and control.
cate_tl = T_learner(X,W,Y,train_idx)
hist(cate_tl)
X-learner
The performance of the T-learner is aggravated if the treatment
assignment is highly unbalanced, i.e. when the majority of observations
in the sample belong to only one treatment status.
Since there are multiple treatment arms, the numbers of treated and
control units are unbalanced, with 1,885 treated units and 3,354 units
in control. Hence, we should expect some form of regularization bias for
\(\hat{m}(1,X)\).
The following implementation is based on a Short
Course by Athey, Spiess and Wager.
The first estimation step is identical to the T-learner. I use
regression forest to fit model \(\hat{m}(1,X)\) in treated and \(\hat{m}(0,X)\) in control: \[\begin{align*}
\hat{m}_0(X) = E[Y| W=0, X] \\
\hat{m}_1(X) = E[Y| W=1, X] \\
\end{align*}\]
For the second stage, I impute the treatment effect for the
control and for the treated using: \[
\begin{align*}
\hat{\tau}(x, 0) &= \hat{m}_1(x, 0) - Y \\
\hat{\tau}(x, 1) &= Y - \hat{m}_0(x, 1) \\\end{align*}
\]
Lastly, I fit two more models to predict these effects. With \(\hat{e}(x)\) being the propensity score
model, one can combine the two second stage models as follows:\[\begin{align*}
\hat{\tau}(x) &= \hat{\tau}(x, 0) \hat{e}(x) + \hat{\tau}(x, 1)
(1-\hat{e}(x))
\end{align*}\]
X-learner is expected to work particularly well in unbalanced
settings, which is often the case in practice as the share of treated
might be restricted financially or otherwise (Okasa, 2022).
Moreover, the X-learner might be less prone to overfitting bias which
would partly justify the full-sample estimation as described in Künzel
et al. (2019).
cate_xl = X_learner(X,W,Y,train_idx,e_rct)
hist(cate_xl)
Causal Forest
The causal forest estimates CATEs using a localized version of
the partially linear estimator:
\[\begin{equation}
\hat{\tau}^{cf}(x) = argmin_{\tau} \left\{ \sum_{i=1}^N \alpha_i(x)
\left[(Y_i - \hat{m}(X_i)) - \tau(x)(W_i - \hat{e}(X_i)) \right]^2
\right\},
\end{equation}
\] where \(\alpha(x)\) are \(x\)-specific weights.
Using predict function, I get out-of-bag estimated
individual ATEs for every individual in the sample:
cf = causal_forest(X_train, Y_train, W_train, W.hat = e_rct)
cate_cf = predict(cf,X_test)$predictions
hist(cate_cf)
The grf package also produces a measure of variable
importance that indicates how often a variable was used in a tree
split.
This can be a rough diagnostic, but it should not be interpreted as
indicating that, for example, variable with low importance is not
related to heterogeneity: if two covariates are highly correlated, the
trees might split on one covariate but not the other, even though both
(or maybe neither) are relevant in the true DGP.
covariates <- c("female", "black", "othrace", "factor(dep)1", "factor(dep)2",
"q2", "q3", "q4", "q5", "q6", "agelt35", "agegt54", "durable",
"lusd", "husd")
var_imp <- c(variable_importance(cf))
names(var_imp) <- covariates
sorted_var_imp <- sort(var_imp, decreasing = TRUE)
for (i in 1:5) {cat(sprintf("%-15s %-10.4f\n", names(sorted_var_imp)[i], sorted_var_imp[i]))}
DR-learner
The DR-learner creates the pseudo-outcome in a first step \[
\begin{align}
\tilde{Y}_{ATE} = \underbrace{\hat{m}(1,X) -
\hat{m}(0,X)}_{\text{outcome predictions}} + \underbrace{\frac{W (Y -
\hat{m}(1,X))}{\hat{e}(X)} - \frac{(1-W) (Y -
\hat{m}(0,X))}{1-\hat{e}(X)}}_{\text{weighted residuals}} \nonumber
\end{align}\]and uses it in a final regression step (a
generic ML problem).
DR-learner is expected to work well in similar situations as the
X-learner with a more balanced treatment assignment, as too extreme
propensity scores might yield the estimator unstable (not an issue for
Penn). It should have an additional advantage over the X-learner due to
its double robustness property.
The simulations of Kennedy (2020) suggest a faster convergence
rate of the DR-learner in comparison to the X- and T-learner.
For the smaller sample sizes (500 and 2′000), the reduction in
the overfitting bias is not large enough in comparison to the bias
stemming from the estimation of the CATE function. For small sample
sizes the additional splitting of the sample does not leave enough
observations to learn the non-linear structure of the CATE (Okasa
2022).
It is well-suited for the nuisance parameters, but should have a
harder time with the linear heterogeneous effects:
cate_dr = DR_learner(X,W,Y,train_idx,e_rct)
hist(cate_dr)
R-learner
The generic version of R-learner is to use the unmodified covariates
in a weighted regression with pseudo outcomes: \[\hat{\tau}^{rl}(x) = argmin_{\tau} \sum_{i=1}^N
\underbrace{(W_i - \hat{e}(X_i))^2}_{\text{weight}}
\left(\underbrace{\frac{Y_i - \hat{m}(X_i)}{W_i -
\hat{e}(X_i)}}_{\text{pseudo-outcome}} - X_i\beta
\right)^2\]
R-learner is expected to work in a settings where the nuisance
functions as well as the CATE function might have a high degree of
complexity. Nie and Wager (2021) show good performance of the R-learner
in settings with complicated nuisance functions and rather simple CATE
function.
The performance of the R-learner is competitive with the other
meta-learners especially in smaller samples, particularly for the
full-sample version (Okasa, 2022).
cate_rl_rf = R_learner(X,W,Y,train_idx,e_rct)
hist(cate_rl_rf)
6. Predict the CATEs in the test sample
Now, let’s compare the estimated effects of the different methods
used so far:
df <- data.frame(
T_learner = cate_tl,
X_learner = cate_xl,
R_learner = cate_rl_rf,
DR_learner = cate_dr,
Causal_Forest = cate_cf)
df_long <- tidyr::gather(df, key = "Method", value = "CATE")
ggplot(df_long, aes(x = CATE, fill = Method)) +
geom_density(alpha = 0.5) +
labs(title = "Densities of Estimated CATE",
x = "Conditional Average Treatment Effect (CATE)",
y = "Density") + theme_minimal() + xlim(c(-1.5, 1.5)) # + ylim(c(0,3.2))
results = cbind(cate_tl,cate_xl,cate_cf,cate_dr,cate_rl_rf)
colnames(results) = c("T-learner","X-learner","Causal Forest","DR-learner","R-learner")
pairs.panels(results,method = "pearson")
describe(results)
All methods are highly correlated. DR and R-learners are more spread
out in the predicted CATE. At first glance, the results look promising,
without unexpected glitches.
7. Evaluating Heterogeneous Effects
One might be tempted to think: “if the histogram is concentrated at a
point, then there is no heterogeneity; if the histogram is spread out,
then our estimator has found interesting heterogeneity.” However, this
may be false.
If the histogram is concentrated at a point, we may be under-powered:
our method was not able to detect any heterogeneity, but maybe it would
detect it if we had more data. If the histogram is spread out, we may be
overfitting: our model is producing very noisy estimates \(\hat{\tau}(x)\), but in fact the true \({\tau}(x)\) can be much smoother as a
function of \(x\).
The goal is to try to assess the validity of the estimated CATEs.
By hand: Best Linear Predictor
Strategy A: Weighted residual BLP
\[(\beta_1, \beta_2) =
argmin_{b_1,b_2}E\{\omega(X)[Y - b_1 (W - e(X)) - b_2 (W - e(X))
(\hat{\tau}(X) - E[\hat{\tau}(X)]) - a \tilde{X}]^2\} \]
where \(\omega(X) = [e(X)
(1-e(X))]^{-1}\).
Strategy B Horvitz-Thompson BLP\[(\beta_1, \beta_2) = argmin_{b_1,b_2}E\{[HY -
b_1 - b_2 (\hat{\tau}(X) - E[\hat{\tau}(X)]) - a H\tilde{X}]^2\}
\]
where \(H =
\frac{W-e(X)}{e(X)(1-e(X))}\). The de-noising component \(\tilde{X}\) in both strategies is \(\hat{m}(0,X)\) obtained by regression
forest.
\(\beta_2 = 0\) if either the
effects are homogeneous or our CATE estimates are “bad.” Thus, rejecting
the hypothesis \(\beta_2 = 0\)
would imply that both the effects are heterogeneous and our CATE
estimates are reliable - evaluCATE
documentation
To compare all methods, I begin by using a modified function
introduced in the Exercise 8. This function takes predicted
CATE, test outcomes, test treatment, and de-noising variables as inputs,
producing the estimated \(\beta_2\)
coefficients and their standard errors as outputs.
# Denoising
rfm0 = regression_forest(X_train[W_train==0,],Y_train[W_train==0])
mhat0_rf = predict(rfm0, X_test)$predictions
BLP_tl = BLP_lecture(cate_tl,Y_test,W_test,mhat0_rf,"T-learner RF")
BLP_xl = BLP_lecture(cate_xl,Y_test,W_test,mhat0_rf,"X-learner RF")
BLP_cf = BLP_lecture(cate_cf,Y_test,W_test,mhat0_rf,"Causal Forest")
BLP_dr = BLP_lecture(cate_dr,Y_test,W_test,mhat0_rf,"DR-learner")
BLP_rl = BLP_lecture(cate_rl_rf,Y_test,W_test,mhat0_rf,"R-learner")
results_BLP = list(BLP_tl,BLP_xl,BLP_cf,BLP_dr,BLP_rl)
names = c("T-learner","X-learner","Causal Forest","DR-learner","R-learner")
plot_BLP(results_BLP, names)
It is worth mentioning that de-noising helps to get more precise
estimates. Nonetheless, the results are quite miserable. Causal Forest
predictions seem to be the best candidate for the
evaluCATE.
Using evaluCATE package
“It’s like putting the real thing on the left hand side, but we
don’t observe the real thing, but can approximate it using an unbiased
signal \(HY\). If our
predictions correlate with this unbiased signal, then we did a good
job”, - M.C. Knaus
# Full sample (train+test) prediction:
eval_cate_cf = predict(cf,X)$predictions
# Logical treatment indicator:
train_idx_l = as.logical(train_idx)
evaluation <- evaluCATE(Y, W, X, eval_cate_cf, train_idx_l, pscore = rep(e_rct, length(Y)))
summary(evaluation, target = "BLP")
The results are as follows:
ATE \(\approx -
0.05\), but never statistically significant.
HTE \(\approx
0.7\), and very noisy. We are partially able to detect the
heterogeneity (and probably slightly overfit the CATE function: \(\beta_2 < 1\)).
The slope \(\beta_2\) is a measure
of how the CATE predictions co-vary with the true CATE. Therefore, the
p-value on the estimate of the coefficient also acts as an omnibus test
for the presence of heterogeneity. If the coefficient is significantly
greater than zero, then we can reject the null hypothesis of no
heterogeneity.
However, coefficients smaller than 0 are not meaningful and should
not be interpreted (source).
GATES
We are interested in the estimation of the GATES for two main reasons:
If the slices are built on the true CATE, we would observe the
following monotonicity:\[ \gamma_1^* \leq
\ldots \leq \gamma_K^* \]where the \(^*\) indicates that the parameter builds on
the true CATE. Therefore, we expect to see the same monotonicity if
\(\hat{\tau}(X)\) provides a good
approximation of \(\tau(X)\).
summary(evaluation, target = "GATES")
plot(evaluation, target = "GATES")
When there isn’t much detectable heterogeneity, the plot above can
end up being non-monotonic. This can mean that the number of
observations is too small for us to be able to detect subgroups with
relevant differences in treatment effect.
In our case, the favorable monotonicity is in question. For most
estimators, we can reject the null hypothesis \(H_0: GATES_5 = GATES_1\) at the
10% significance level, but not at the
5% level.
TOC curve
We are interested in the presence of heterogeneity and how much
benefit there is to prioritizing treatment based on this heterogeneity.
By “benefit” one means the expected increase in outcomes from giving
treatment to a fraction of the population with the \(\hat{\tau}(x)\) as opposed to giving
treatment to a randomly selected fraction of the same size.
The TOC is defined as the Total Outcome Contrast, expressed as:
\[ TOC(u; \hat{\tau}) = E[Y(1) - Y(0) |
F(\hat{\tau}(X)) \geq 1 - u] - E[Y(1) - Y(0)] \]
This measure contrasts the average effect in the subgroup with the
top 100u% highest estimated Conditional Average Treatment
Effects (CATEs) with the Average Treatment Effect (ATE).
If the estimated CATEs effectively capture systematic heterogeneity,
we anticipate:
the TOC to be positive throughout, indicating that the average
effect in the subgroup with high estimated CATEs consistently exceeds
the overall average treatment effect.
the TOC to be largest for low values of u,
signifying that the contrast in average effects is most pronounced when
focusing on the subgroup with the highest estimated CATEs.
plot(evaluation, target = "TOC")
If there is scarcely any heterogeneity in \(\tau(X_i)\), this area will be vanishingly
small. This is practically what is observed. There are
barely any benefits to stratifying treatment.
A null effect can be attributed to
- Low power, as perhaps the sample size is not large enough to detect
HTEs,
- That the HTE estimator does not detect them, or
- The heterogeneity in the treatment effects along observable
predictor variables are negligible.
I would like evaluCATE to include an option to restrict
the Y-axis. In this case, the initial instability makes the analysis
challenging.
Turning a blind eye to the TOC curve, the results seem to be modestly
significant and even promising. Heterogeneity is uncovered, ladies and
gentlemen. This assignment was truly a pleasure.
Causal Inference meme knowledge wasn’t in
vain
Was any heterogeneity uncovered, though? Or is this a case of seed
hacking and pure luck?
8. Results variability
To explore variability, I perform 100 “Monte Carlo” simulations—i.e.,
I run the entire assignment 100 times using different seeds. The
parameter of interest is \(\beta_2\) in
BLP. I count how many times its confidence intervals cover zero,
employing both Weighted Residual and Horvitz-Thompson BLPs (with
de-noising).
Additionally, I conduct the same operation for three different sample
splitting rules: 50/50, 70/30, and 30/70.
Each column takes around 40 minutes to run; therefore, the code is
commented.
# coverage = monte_carlo(X,W,Y,iterations = 100,train_share = 0.5)
# coverage
Table 2: Third Treatment Arm - Multiple
Estimations with Varying Seeds and Train/Test Splitting
| T-learner Weight |
0.11 |
0.15 |
0.13 |
| T-learner HT |
0.11 |
0.15 |
0.17 |
| X-learner Weight |
0.08 |
0.19 |
0.20 |
| X-learner HT |
0.09 |
0.20 |
0.19 |
| Causal Forest Weight |
0.13 |
0.17 |
0.13 |
| Causal forest HT |
0.14 |
0.18 |
0.10 |
| DR-learner Weight |
0.03 |
0.10 |
0.13 |
| DR-learner HT |
0.03 |
0.11 |
0.15 |
| R-learner Weight |
0.03 |
0.07 |
0.13 |
| R-learner HT |
0.04 |
0.10 |
0.14 |
We are able to detect heterogeneous effects only sporadically. Either
the effects are homogeneous or CATE estimates are not reliable.
9. Moving the goalpost
To reiterate: the last section has shown that there is either no
heterogeneity or that we are unable to detect it. Detection issues arise
from either a weak signal or insufficient sample size (as we know,
Machine Learning methods are data-hungry).
In the following section, to “boost power,” I would argue that the
treatments are quite similar. One can combine them into “no treatment”
and “some treatment,” resulting in an increased sample size:
10,559 treated and 3,354 in the
control group.
The parts for the combined treatment are commented throughout the
code to keep the assignment concise (and to manage the running time).
Simulated results are provided in the tables.
Table 3: ATE Estimated by Naive Mean
Comparison
| Treatment 1 |
-0.0274 |
0.0386 |
-0.70 |
0.478 |
| Treatment 2 |
-0.0788 |
0.0325 |
-2.42 |
0.0153 |
| Treatment 3 |
-0.0671 |
0.0353 |
-1.89 |
0.0576 |
| Treatment 4 |
-0.0854 |
0.0358 |
-2.38 |
0.0171 |
| Treatment 5 |
-0.0335 |
0.0356 |
-0.94 |
0.3466 |
| Treatment 6 |
-0.0715 |
0.0391 |
-1.82 |
0.0678 |
| Combined treatment |
-0.0623 |
0.0240 |
-2.58 |
0.0096 |
The idea of similar treatments is debatable, to say the least, but
let’s keep it for the sake of the argument.
As an illustration, best linear predictor with combined treatment and
a 70% training sample gives the following result:

Performing 100 ‘Monte Carlo’ simulations (takes the most time due to
a 3-fold increased sample size) again, we observe:
# coverage = monte_carlo(X,W,Y,iterations = 100,train_share = 0.3)
# coverage
Table 4: Combined Treatment - Multiple
Estimations with Varying Seeds and Train/Test Splitting
| T-learner Weight |
0.05 |
0.03 |
0.04 |
| T-learner HT |
0.04 |
0.02 |
0.04 |
| X-learner Weight |
0.05 |
0.02 |
0.05 |
| X-learner HT |
0.05 |
0.02 |
0.06 |
| Causal Forest Weight |
0.05 |
0.02 |
0.07 |
| Causal Forest HT |
0.04 |
0.03 |
0.07 |
| DR-learner Weight |
0.03 |
0.02 |
0.03 |
| DR-learner HT |
0.02 |
0.01 |
0.03 |
| R-learner Weight |
0.03 |
0.02 |
0.02 |
| R-learner HT |
0.02 |
0.02 |
0.02 |
Here, I shall be very delicate with my interpretations. As they say
in movies, “anything you say can and will be used against you in a court
of law”.
Firstly, only 100 simulations were conducted. Using 95% confidence
intervals, the numbers in the table closely align with the probability
of a type 1 error (false positive). Personally, I have more confidence
in rejecting the presence of heterogeneity.
Simultaneously, one could argue that now there is more treatment
heterogeneity among different treatment arms than within a single
treatment arm. Instead of adding power, the standard errors have
increased, leading to a reduction in power.
In conclusion, the data have been prepared and
analyzed using multiple Causal Machine Learning methods. The goal was to
detect whether treatment effect heterogeneity is present.
In fact, the Average Treatment (program participation) was not
significant at the 5% level, and the treatment variable also showed low
predictive power for the outcome variable (unemployment spell).
Subsequently, the estimated conditional average treatment effects
(CATE) were analyzed using best linear predictor, GATES, and TOC
methods. Monte Carlo simulations indicate that there is no treatment
effect heterogeneity (or that we were not able to detect it).
10. evaluCATE experience
To be honest, I do not have enough experience to distinguish between
a good and bad R package. I didn’t experience any code issues;
everything was working from the start. I would wish for more pleasing
visual output (GATES looks confusing), but this is only a minor
point.
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAyIgpzdWJ0aXRsZTogIlJlLWVtcGxveW1lbnQgZGF0YXNldCwgdHJlYXRtZW50IDMgKOKEljUpIgphdXRob3I6ICJSb21hbiBSYWtvdiwgNjI5NDc3OSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCjxicj4KCipUaGlzIGlzIHRoZSBmaXJzdCB0aW1lIEkgYW0gdXNpbmcgYW4gUiBwcm9qZWN0LCBhbmQgSSBhcG9sb2dpemUgZm9yIHN1Ym1pdHRpbmcgYSBwbGV0aG9yYSBvZiBmaWxlcyBpbiB0aGUgYXJjaGl2ZS4qCgoqVG8gaW5zdGFsbCB0aGUgd3JpdHRlbiBwYWNrYWdlLCBJIHVzZSAnYnVpbGQnIC1cPiAnbW9yZScgLVw+ICdsb2FkIGFsbC4nIEFnYWluLCBJIGFwb2xvZ2l6ZSBmb3IgYW55IGNsdW5raW5lc3MuKgoKPGJyPgoKIyMjIyBBIHdvcmQgb2YgaW50cm9kdWN0aW9uCgpUaGUgImV4cGxvcmluZyB2YXJpYWJpbGl0eSIgc2VjdGlvbiBpcyBjb21tZW50ZWQgb3V0IGR1ZSB0byBpdHMgbGVuZ3RoeSBydW50aW1lICh0YWtlcyBhbiBob3VyIGZvciBlYWNoIGNvbmZpZ3VyYXRpb24pLiBTaW1pbGFybHksIHRoZSAiY29tYmluZWQgdHJlYXRtZW50IiBwYXJ0IGlzIGFsc28gY29tbWVudGVkIGZvciBicmV2aXR5IChidXQgY2FuIGJlIGVhc2lseSBleGVjdXRlZCkuIFRoZSBvdXRjb21lcyBvZiB0aGVzZSBjb21tZW50ZWQgc2VjdGlvbnMgYXJlIGRldGFpbGVkIGluIHRoZSB0ZXh0IHRhYmxlcy4KCkFkZGl0aW9uYWxseSwgSSBoYXZlIGRldmVsb3BlZCBhbiBSIHBhY2thZ2UgY2FsbGVkIGBibHVlYmVycnlgLCBjb250YWluaW5nIGFsbCBsZWFybmVycyAoZXhjZXB0IENhc3VhbCBGb3Jlc3QpLCBhbG9uZyB3aXRoIHZpc3VhbGl6YXRpb25zIGFuZCBzaW11bGF0aW9ucy4gQnJpZWYgZG9jdW1lbnRhdGlvbiBpcyBwcm92aWRlZCBmb3IgbW9zdCBvZiB0aGUgZnVuY3Rpb25zLgoKPGJyPgoKIyMgMS4gUHJlcGFyZSBhbmQgZXhwbG9yZSB0aGUgW2RhdGFdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vY29kZS92aWN0b3JjaGVybm96aHVrb3YvYW5hbHl6aW5nLXJjdC1yZWVtcGxveW1lbnQtZXhwZXJpbWVudC9ub3RlYm9vaykKCkkgYW5hbHl6ZSB0aGUgUGVubnN5bHZhbmlhIHJlLWVtcGxveW1lbnQgYm9udXMgZXhwZXJpbWVudCAoQmlsaWFzLCAyMDAwOyBDaGVybm96aHVrb3YsIDIwMTgpLgoKYGBge3Igd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGhkbSkKbGlicmFyeShncmYpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoZXZhbHVDQVRFKQpsaWJyYXJ5KGVzdGltYXRyKQpsaWJyYXJ5KGxtdGVzdCkKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShzdGFyZ2F6ZXIpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpQZW5uIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZSgiZGF0YS9wZW5uX2phZS5kYXQiLCBoZWFkZXIgPSBUUlVFKSkKCiMgVG8gc3R1ZHkgYWxsIHRyZWF0bWVudCBhcm1zIHNpbXVsdGFuZW91c2x5IFstLT4gQ09NTUVOVF06IApQZW5uICAgIDwtIHN1YnNldChQZW5uLCB0Zz09MyB8IHRnPT0wKSAgICAgICAgICAgIyBLZWVwIG9ubHkgdGhlIDNyZCB0cmVhdG1lbnQgb3B0aW9uClBlbm4kdGcgPC0gaWZlbHNlKFBlbm4kdGcgPT0gMywgMSwgUGVubiR0ZykgICAgICAjIG1ha2UgaXQgezAsM30gLS0+IHswLDF9IAoKIyBUbyBzdHVkeSBhbGwgdHJlYXRtZW50IGFybXMgc2ltdWx0YW5lb3VzbHkgWy0tPiBVTkNPTU1FTlRdOiAKIyBQZW5uJHRnIDwtIGlmZWxzZShQZW5uJHRnICVpbiUgYygyLCAzLCA0LCA1LCA2KSwgMSwgUGVubiR0ZykKCmF0dGFjaChQZW5uKQpgYGAKCjxicj4KClRoZXNlIGV4cGVyaW1lbnRzIHdlcmUgY29uZHVjdGVkIGluIHRoZSAxOTgwcyBieSB0aGUgVS5TLiBEZXBhcnRtZW50IG9mIExhYm9yIHRvIHRlc3QgdGhlIGluY2VudGl2ZSBlZmZlY3RzIG9mIGFsdGVybmF0aXZlIGNvbXBlbnNhdGlvbiBzY2hlbWVzIGZvciB1bmVtcGxveW1lbnQgaW5zdXJhbmNlIChVSSk6IFVJIGNsYWltYW50cyB3ZXJlIHJhbmRvbWx5IGFzc2lnbmVkIGVpdGhlciB0byBhIGNvbnRyb2wgZ3JvdXAgb3Igb25lIG9mIHNpeCB0cmVhdG1lbnQgZ3JvdXBzLgoKSSBmb2N1cyBvbiAqKnRyZWF0bWVudCBncm91cCAzKiosIHNwZWNpZmljYWxseSwgdGhlIG9uZSB3aXRoIGEgKipoaWdoKiogYm9udXMgYW5kICoqc2hvcnQqKiBxdWFsaWZpY2F0aW9uOgoKfCBHcm91cCAgICAgICAgICAgfCBCb251cyBBbW91bnQgICAgICAgICAgICAgfCBRdWFsaWZpY2F0aW9uIFBlcmlvZCB8IFdvcmtzaG9wIE9mZmVyIHwKfC0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLXwKfCBDb250cm9sIEdyb3VwICAgfCAwICAgICAgICAgICAgICAgICAgICAgICAgfCAwICAgICAgICAgICAgICAgICAgICB8IE5vICAgICAgICAgICAgIHwKfCBUcmVhdG1lbnQgMSAgICAgfCBMb3cgKDMgeCBXQkEpICAgICAgICAgICAgfCBTaG9ydCAoNiBXZWVrcykgICAgICB8IFllcyAgICAgICAgICAgIHwKfCBUcmVhdG1lbnQgMiAgICAgfCBMb3cgKDMgeCBXQkEpICAgICAgICAgICAgfCBMb25nICgxMiBXZWVrcykgICAgICB8IFllcyAgICAgICAgICAgIHwKfCAqKlRyZWF0bWVudCAzKiogfCAqKkhpZ2ggKDYgeCBXQkEpKiogICAgICAgfCAqKlNob3J0ICg2IFdlZWtzKSoqICB8ICoqWWVzKiogICAgICAgIHwKfCBUcmVhdG1lbnQgNCAgICAgfCBIaWdoICg2IHggV0JBKSAgICAgICAgICAgfCBMb25nICgxMiBXZWVrcykgICAgICB8IFllcyAgICAgICAgICAgIHwKfCBUcmVhdG1lbnQgNSAgICAgfCBJbml0aWFsbHkgSGlnaCAoNiB4IFdCQSkgfCBMb25nICgxMiBXZWVrcykgICAgICB8IFllcyAgICAgICAgICAgIHwKfCBUcmVhdG1lbnQgNiAgICAgfCBIaWdoICg2IHggV0JBKSAgICAgICAgICAgfCBMb25nICgxMiBXZWVrcykgICAgICB8IE5vICAgICAgICAgICAgIHwKCjogKipUYWJsZSAxKio6IFRyZWF0bWVudCBBc3NpZ25tZW50cwoKSW4gdGhlIGNvbnRyb2wgZ3JvdXAgdGhlIGN1cnJlbnQgcnVsZXMgb2YgdGhlIFVJIGFwcGxpZWQuIEluZGl2aWR1YWxzIGluIHRoZSB0cmVhdG1lbnQgZ3JvdXBzIHdlcmUgb2ZmZXJlZCBhIGNhc2ggYm9udXMgaWYgdGhleSBmb3VuZCBhIGpvYiB3aXRoaW4gc29tZSBwcmUtc3BlY2lmaWVkIHBlcmlvZCBvZiB0aW1lIChxdWFsaWZpY2F0aW9uIHBlcmlvZCksIHByb3ZpZGVkIHRoYXQgdGhlIGpvYiB3YXMgcmV0YWluZWQgZm9yIGEgc3BlY2lmaWVkIGR1cmF0aW9uLgoKVGhlIG1hdHJpeCBvZiBjb3ZhcmlhdGVzLCAkWCQsIGNvbnNpc3RzIG9mIGFnZSBncm91cCBkdW1taWVzLCBnZW5kZXIsIHJhY2UsIHRoZSBudW1iZXIgb2YgZGVwZW5kZW50cywgcXVhcnRlciBvZiB0aGUgZXhwZXJpbWVudCwgbG9jYXRpb24gd2l0aGluIHRoZSBzdGF0ZSwgZXhpc3RlbmNlIG9mIHJlY2FsbCBleHBlY3RhdGlvbnMgYW5kIHR5cGUgb2Ygb2NjdXBhdGlvbjoKCnwgVmFyaWFibGUgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IGBhYmR0YCAgICAgICB8IENocm9ub2xvZ2ljYWwgdGltZSBvZiBlbnJvbGxtZW50IG9mIGVhY2ggY2xhaW1hbnQgaW4gdGhlIGV4cGVyaW1lbnQgICAgICAgICAgICB8CnwgYHRnYCAgICAgICAgIHwgVGhlIHRyZWF0bWVudCBncm91cCAoYm9udXMgYW1vdW50IC0gcXVhbGlmaWNhdGlvbiBwZXJpb2QpIG9mIGVhY2ggY2xhaW1hbnQgICAgIHwKfCBgaW51aWR1cjFgICAgfCBBIG1lYXN1cmUgb2YgdGhlIGxlbmd0aCAoaW4gd2Vla3MpIG9mIHRoZSBmaXJzdCBzcGVsbCBvZiB1bmVtcGxveW1lbnQgICAgICAgICAgfAp8IGBmZW1hbGVgICAgICB8IER1bW15IHZhcmlhYmxlOyBpdCBpbmRpY2F0ZXMgaWYgdGhlIGNsYWltYW50J3Mgc2V4IGlzIGZlbWFsZSAoPTEpIG9yIG1hbGUgKD0wKSB8CnwgYGJsYWNrYCAgICAgIHwgRHVtbXkgdmFyaWFibGU7IGl0IGluZGljYXRlcyBhIHBlcnNvbiBvZiBibGFjayByYWNlICg9MSkgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgaGlzcGFuaWNgICAgfCBEdW1teSB2YXJpYWJsZTsgaXQgaW5kaWNhdGVzIGEgcGVyc29uIG9mIEhpc3BhbmljIHJhY2UgKD0xKSAgICAgICAgICAgICAgICAgICAgfAp8IGBvdGhyYWNlYCAgICB8IER1bW15IHZhcmlhYmxlOyBpdCBpbmRpY2F0ZXMgYSBub24td2hpdGUsIG5vbi1ibGFjaywgbm90LUhpc3BhbmljICg9MSkgICAgICAgICB8CnwgYGRlcGAgICAgICAgIHwgVGhlIG51bWJlciBvZiBkZXBlbmRlbnRzIG9mIGVhY2ggY2xhaW1hbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgcTEtcTZgICAgICAgfCBTaXggZHVtbXkgdmFyaWFibGVzOiB0aGUgcXVhcnRlciBvZiBlbnJvbGxtZW50ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGByZWNhbGxgICAgICB8IDEgaWYgYW5zd2VyZWQgInllcyIgd2hlbiBhc2tlZCBpZiBoYWQgYW55IGV4cGVjdGF0aW9uIHRvIGJlIHJlY2FsbGVkICAgICAgICAgICB8CnwgYGFnZWx0MzVgICAgIHwgMSBpZiB0aGUgY2xhaW1hbnQncyBhZ2UgaXMgbGVzcyB0aGFuIDM1ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgYWdlZ3Q1NGAgICAgfCAxIGlmIHRoZSBjbGFpbWFudCdzIGFnZSBpcyBtb3JlIHRoYW4gNTQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBkdXJhYmxlYCAgICB8IDEgaWYgdGhlIG9jY3VwYXRpb24gb2YgdGhlIGNsYWltYW50IHdhcyBpbiB0aGUgc2VjdG9yIG9mIGR1cmFibGUgbWFudWZhY3R1cmluZyB8CnwgYG5vbmR1cmFibGVgIHwgMSBpZiB0aGUgY2xhaW1hbnQgd29ya2VkIGluIHRoZSBzZWN0b3Igb2Ygbm9uLWR1cmFibGUgbWFudWZhY3R1cmluZyAgICAgICAgICAgIHwKfCBgbHVzZGAgICAgICAgfCAxIGlmIGZpbGVkIGluIGFyZWFzIG9mIGxvdyB1bmVtcC4gcmF0ZSBhbmQgc2hvcnQgZHVyYXRpb24gb2YgdW5lbXBsb3ltZW50ICAgICAgfAp8IGBodXNkYCAgICAgICB8IDEgaWYgZmlsZWQgaW4gYXJlYXMgb2YgaGlnaCB1bmVtcC4gcmF0ZSBhbmQgc2hvcnQgZHVyYXRpb24gb2YgdW5lbXBsb3ltZW50ICAgICB8CnwgYG11bGRgICAgICAgIHwgMSBpZiBmaWxlZCBpbiBhcmVhcyBvZiBtb2RlcmF0ZSB1bmVtcC4gcmF0ZSBhbmQgbG9uZyBkdXJhdGlvbiBvZiB1bmVtcGxveW1lbnQgIHwKCjxicj4KCiMjIDIuIERlc2NyaXB0aXZlIHN0YXRpc3RpY3MgJiBzaW1wbGUgQVRFCgpUaGUgZGF0YSBjb21lcyBmcm9tIGFuIGV4cGVyaW1lbnRhbCAoUkNUKSBzZXR0aW5nOgoKYGBge3J9CmhlYWQoUGVubikKYGBgCgpJbnRlcmVzdGluZ2x5LCB0aGVyZSBhcmUgbm8gY29udGludW91cyB2YXJpYWJsZXMgYXBhcnQgZnJvbSB0aGUgcmVzcG9uc2UgdmFyaWFibGUuCgo8YnI+CgojIyMgQ292YXJpYXRlIGJhbGFuY2UgY2hlY2sKClRoaXMgaXMgZG9uZSB3aXRoIHRoZSBFaWNoZXItSHViZXItV2hpdGUgKGhldGVyb3NjZWRhc3RpY2l0eSBhZGp1c3RlZCkgc3RhbmRhcmQgZXJyb3JzLCBpbmNsdWRpbmcgdGhlIGxpbmVhciB0ZXJtcyBhcyB3ZWxsIGFzIHRoZSBzZWNvbmQgb3JkZXIgaW50ZXJhY3Rpb25zOgoKYGBge3Igd2FybmluZz1GQUxTRX0KbSA8LSBsbSh0Z34oZmVtYWxlK2JsYWNrK290aHJhY2UrZmFjdG9yKGRlcCkrcTIrcTMrcTQrcTUrcTYrYWdlbHQzNSthZ2VndDU0K2R1cmFibGUrbHVzZCtodXNkKV4yKQptIDwtIGNvZWZ0ZXN0KG0sIHZjb3YgPSB2Y292SEMobSwgdHlwZT0iSEMxIikpCgpzdGFyZ2F6ZXIobVsxOjE1LF0sIHR5cGUgPSAidGV4dCIsdGl0bGUgPSAnQ292YXJpYXRlIGJhbGFuY2UgY2hlY2snKQpgYGAKCk9ubHkgYSBwb3J0aW9uIGlzIHByaW50ZWQgZm9yIGJyZXZpdHkuIEdlbmVyYWxseSwgdGhlIGNvdmFyaWF0ZXMgYXJlIGJhbGFuY2VkLCB3aXRoIHZlcnkgZmV3IGV4Y2VwdGlvbnMuIEFjY29yZGluZyB0byB0aGUgRi10ZXN0LCB0aGUgY292YXJpYXRlcyBhcmUgbm90IGpvaW50bHkgc2lnbmlmaWNhbnQgYXQgYWxsIGNvbnZlbnRpb25hbCBzaWduaWZpY2FuY2UgbGV2ZWxzOgoKYGBgICAgICAgICAgCkYtc3RhdGlzdGljOiAwLjk4MjUgb24gMTAyIGFuZCA1MTM2IERGLCAgcC12YWx1ZTogMC41MzE1CmBgYAoKSSBhc3N1bWUgdGhhdCB0aGUgUkNUIGFzc3VtcHRpb25zIGhvbGQuCgo8YnI+CgojIyMgU2ltcGxlIEFURSBlc3RpbWF0aW9uCgpUbyBldmFsdWF0ZSB0aGUgaW1wYWN0IG9mIHRoZSB0cmVhdG1lbnQgb24gdW5lbXBsb3ltZW50IGR1cmF0aW9uLCBJIGNvbnNpZGVyIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbDoKCiQkIFkgPSBXXGJldGFfMSArIFxtYXRoYmZ7WH0nXGJldGFfMiArIFxlcHNpbG9uLCBccXVhZCBFKFxlcHNpbG9uIHwgVywgXG1hdGhiZntYfScpJyA9IDAsICQkCgp3aGVyZSAkWSQgaXMgdGhlICoqbG9nKiogb2YgdGhlIGR1cmF0aW9uIG9mIHVuZW1wbG95bWVudCwgJFckIGlzIGEgdHJlYXRtZW50IGluZGljYXRvciwgYW5kICRcbWF0aGJme1h9JCBpcyBhIHNldCBvZiBjb250cm9scyBkZWZpbmVkIHByZXZpb3VzbHkuIEhlcmUgJFxiZXRhXzEkIGlzIHRoZSBBVEUsIGlmIHRoZSBSQ1QgYXNzdW1wdGlvbnMgaG9sZC4KCkkgYWxzbyBjb25zaWRlciBhbiBpbnRlcmFjdGl2ZSByZWdyZXNzaW9uIG1vZGVsOgoKJCQgWSA9IFdcYWxwaGFfMSArIFcgXG1hdGhiZntYfSdcYWxwaGFfMiArIFxtYXRoYmZ7WH0nXGJldGFfMiArIFxlcHNpbG9uLCBccXVhZCBFKFxlcHNpbG9uIHwgVywgXG1hdGhiZntYfScsIFdcbWF0aGJme1h9JyknID0gMCwgJCQKCndoZXJlICRcbWF0aGJme1h9JyQncyBhcmUgZGVtZWFuZWQgKGFwYXJ0IGZyb20gdGhlIGludGVyY2VwdCksIHNvIHRoYXQgJFxhbHBoYV8xJCBpcyB0aGUgQVRFLCBpZiB0aGUgUkNUIGFzc3VtcHRpb25zIGhvbGQuCgpVbmRlciBSQ1QsIHRoZSBwcm9qZWN0aW9uIGNvZWZmaWNpZW50ICRcYmV0YV8xJCBoYXMgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBjYXVzYWwgZWZmZWN0IG9mIHRoZSB0cmVhdG1lbnQgb24gdGhlIGF2ZXJhZ2Ugb3V0Y29tZSAoQVRFKS4gVGhlIGNvdmFyaWF0ZXMgaGVyZSBhcmUgaW5kZXBlbmRlbnQgb2YgdGhlIHRyZWF0bWVudCAkVyQsIHNvIHdlIGNhbiBpZGVudGlmeSAkXGJldGFfMSQgYnkganVzdCBsaW5lYXIgcmVncmVzc2lvbiBvZiAkWSQgb24gJFckLCB3aXRob3V0IGFkZGluZyBjb3ZhcmlhdGVzLiBIb3dldmVyLCBJIGRvIGFkZCBjb3ZhcmlhdGVzIHRvIGltcHJvdmUgdGhlIHByZWNpc2lvbiBvZiB0aGUgQVRFIGVzdGltYXRlcy4KCkkgc3BlY2lmeSBmb3VyIG1vZGVsczoKCjEuICBTaW1wbGUgdHJlYXRlZC1jb250cm9sIG1lYW4gY29tcGFyaXNvbiwKCjIuICBBZGRpbmcgY292YXJpYXRlcyBhcyBjb250cm9scywKCjMuICBSdW5uaW5nIGFuIGludGVyYWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWwgKCppbmNsdWRpbmcgYWxsIHZhcmlhYmxlcyBhbmQgc2Vjb25kLW9yZGVyIGludGVyYWN0aW9ucyopLAoKNC4gIFBhcnRpYWxsaW5nIG91dCB3aXRoIExhc3NvLgoKTGFzdCB0d28gbWV0aG9kcyBhcmUgdGFrZW4gZnJvbSBWaWt0b3IgQ2hlcm5venVrb3YncyBba2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2NvZGUvdmljdG9yY2hlcm5vemh1a292L2FuYWx5emluZy1yY3QtcmVlbXBsb3ltZW50LWV4cGVyaW1lbnQvbm90ZWJvb2spLgoKVGhlIG9taXR0ZWQgZHVtbWllcywgZW5zdXJpbmcgbm8gcGVyZmVjdCBtdWx0aWNvbGxpbmVhcml0eSwgYXJlOiBgcTFgLCBgbm9uZHVyYWJsZWAsIGBtdWxkYC4KCmBgYHtyfQojIDFdIE5haXZlIG1lYW4gY29tcGFyaXNvbiAKb2xzLnNpbXBsZSAgPSAgbG0obG9nKGludWlkdXIxKX50ZykKb2xzLnNpbXBsZSAgPSAgY29lZnRlc3Qob2xzLnNpbXBsZSwgdmNvdiA9IHZjb3ZIQyhvbHMuc2ltcGxlLCB0eXBlPSJIQzEiKSkKCiMgMl0gUmVncmVzc2lvbiB3aXRoIGNvbnRyb2xzCm9scy5jb250cm9sID0gbG0obG9nKGludWlkdXIxKX50ZysgKGZlbWFsZStibGFjaytvdGhyYWNlK2ZhY3RvcihkZXApK3EyK3EzK3E0K3E1K3E2K2FnZWx0MzUrYWdlZ3Q1NCtkdXJhYmxlK2x1c2QraHVzZCleMikKb2xzLmNvbnRyb2wgPSBjb2VmdGVzdChvbHMuY29udHJvbCwgdmNvdiA9IHZjb3ZIQyhvbHMuY29udHJvbCwgdHlwZT0iSEMxIikpCgp4ID0gbW9kZWwubWF0cml4ICh+IChmZW1hbGUrYmxhY2srb3RocmFjZStmYWN0b3IoZGVwKStxMitxMytxNCtxNStxNithZ2VsdDM1K2FnZWd0NTQrZHVyYWJsZStsdXNkK2h1c2QpXjIpWywtMV0KeCA9IHNjYWxlKHgsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpCgojIDNdIFBhcnRpYWxsaW5nIG91dCB3aXRoIGxhc3NvCnRnX2RlbWVhbiAgPSB0ZyAtIG1lYW4odGcpCkR4ICAgICAgICAgPSBtb2RlbC5tYXRyaXgofnRnX2RlbWVhbip4KVssLTFdCnJsYXNzby5pcmEgPSBzdW1tYXJ5KHJsYXNzb0VmZmVjdHMoRHgsIGxvZyhpbnVpZHVyMSksIGluZGV4ID0gMSkpCgojIDRdIEludGVyYWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWwgCm9scy5pcmEgPSBsbShsb2coaW51aWR1cjEpIH4gdGcqeCkgCm9scy5pcmEgPSBjb2VmdGVzdChvbHMuaXJhLCB2Y292ID0gdmNvdkhDKG9scy5pcmEsIHR5cGU9IkhDMSIpKQoKb2xzLmlyYV9vcmQ8LSBvbHMuaXJhW29yZGVyKG9scy5pcmFbLCAiUHIoPnx0fCkiXSksIF0Kc3RhcmdhemVyKG9scy5pcmFfb3JkWzE6MTUsXSwgdHlwZSA9ICJ0ZXh0Iix0aXRsZSA9ICdJbnRlcmFjdGl2ZSByZWdyZXNzaW9uIG1vZGVsJykKYGBgCgoqKlNvcnRlZCBieSBwLXZhbHVlKiouIE9ubHkgYSBwYXJ0IGlzIHByaW50ZWQgZm9yIGJyZXZpdHkuIEJhc2VkIG9uIHRoZSBJUkEgcmVzdWx0cyBvbmUgbWF5IGNvbmNsdWRlIHRoYXQgdGhlcmUgaXMgYSBwb3NzaWJsZSB0cmVhdG1lbnQgZWZmZWN0IGhldGVyb2dlbmVpdHk6IHNvbWUgaW50ZXJhY3Rpb25zIGJldHdlZW4gJFgkIGFuZCAkVyQgYXJlIHNpZ25pZmljYW50LgoKYGBge3J9Ck9MU19yZXN1bHRzKG9scy5zaW1wbGUsIG9scy5jb250cm9sLCBvbHMuaXJhLCBybGFzc28uaXJhKQpgYGAKClRyZWF0bWVudCBncm91cCDihJYzIGV4cGVyaWVuY2VzIGFuIGF2ZXJhZ2UgZGVjcmVhc2Ugb2YgYWJvdXQgKio1LjglKiogaW4gdGhlIGxlbmd0aCBvZiB1bmVtcGxveW1lbnQgc3BlbGwsIGFsYmVpdCB0aGUgZWZmZWN0cyBhcmUgbm90IHNpZ25pZmljYW50IGF0IHRoZSA1JSBsZXZlbC4KCldlIGFsc28gc2VlIHRoZSByZWdyZXNzaW9uIGVzdGltYXRvcnMgb2ZmZXIgc2xpZ2h0bHkgbG93ZXIgZXN0aW1hdGVzOiB0aGUgZGlmZmVyZW5jZSBpcyBwZXJoYXBzIGR1ZSB0byBtaW5vciBpbWJhbGFuY2UgaW4gdGhlIHRyZWF0bWVudCBhbGxvY2F0aW9uLCB3aGljaCB0aGUgcmVncmVzc2lvbiBlc3RpbWF0b3JzIHRyeSB0byBjb3JyZWN0LgoKWyoqU3R1ZHkncyBbcmVwb3J0XShodHRwczovL2NsZWFyLmRvbC5nb3YvU3R1ZHkvUGVubnN5bHZhbmlhLVJlZW1wbG95bWVudC1Cb251cy1EZW1vbnN0cmF0aW9uLWZpbmFsLXJlcG9ydC1Db3Jzb24tZXQtYWwtMTk5MikgY29uY2x1c2lvbjoqKl17LnVuZGVybGluZX1cCkVzdGltYXRlcyBiYXNlZCBvbiBkYXRhIGZyb20gVUkgd2FnZSByZWNvcmRzIGZhaWwgdG8gcHJvdmlkZSBhbnkgZXZpZGVuY2UgdGhhdCB0aGUgYm9udXMgb2ZmZXJzIGVuaGFuY2VkIHRoZSBlbXBsb3ltZW50IG9mIGNsYWltYW50cyBmb2xsb3dpbmcgdGhlIGJlbmVmaXQgYXBwbGljYXRpb24gZGF0ZS4KCjxicj4KCiMjIDMuIE1vZGVsaW5nIGNob2ljZXMgYW5kIGhldGVyb2dlbmVpdHkgZGV0ZWN0aW9uCgojIyMgVGhlIGNob2ljZSBvZiBwcm9wZW5zaXR5IHNjb3JlCgpGaXJzdGx5LCBJIGRlZmluZSB0aGUgc3RhbmRhcmQgdmFyaWFibGVzIG5lY2Vzc2FyeSBmb3IgZnV0dXJlIGVzdGltYXRpb24gKCRZLFgsVyQpOgoKYGBge3J9CiMgUHJvZHVjZSBtYXRyaXggb2YgY292YXJpYXRlcyBYLCB0cmVhdG1lbnQgaW5kaWNhdG9yIFcgYW5kIG91dGNvbWUgdmVjdG9yIFk6IApYIDwtIG1vZGVsLm1hdHJpeCh+IChmZW1hbGUrYmxhY2srb3RocmFjZStmYWN0b3IoZGVwKStxMitxMytxNCtxNStxNithZ2VsdDM1K2FnZWd0NTQrZHVyYWJsZStsdXNkK2h1c2QpKVssLTFdClcgPC0gdGcKWSA8LSBsb2coaW51aWR1cjEpCmBgYAoKSW4gdGhlIHRoaXJkIHRyZWF0bWVudCBhcm0sIHRoZXJlIGFyZSAxLDg4NSB0cmVhdGVkIHVuaXRzIGFuZCAzLDM1NCB1bml0cyBpbiB0aGUgY29udHJvbCBncm91cDoKCmBgYHtyfQp0YWJsZSh0ZykKYGBgCgpBcyB0aGUgdHJlYXRtZW50IGlzIHJhbmRvbWx5IGFzc2lnbmVkLCBJIHVzZSB0aGUgZnJhY3Rpb24gb2YgdHJlYXRlZCBhcyB0aGUgZXN0aW1hdG9yIG9mIHRoZSBwcm9wZW5zaXR5IHNjb3JlIChmb2xsb3dzIENoZXJub3podWtvdiBldCBhbC4sMjAxOCkuIEhlbmNlLCB0aGUgcHJvcGVuc2l0eSBzY29yZSBpcyBjb25zdGFudDogdGhpcyB2YWx1ZSBpcyB1c2VkIGJ5IGxlYXJuZXJzIGZvciB0aGUgZXN0aW1hdGlvbiBvZiBDQVRFLgoKYGBge3J9CiMgZV9yY3QgPSByZXAoZV9yY3QsIGxlbmd0aChZKSkKZV9yY3QgPSBtZWFuKFcpCmVfcmN0CmBgYAoKVG8gZG91YmxlIGNoZWNrOiBwcm9wZW5zaXR5IHNjb3JlcyBlc3RpbWF0ZWQgYnkgcmVncmVzc2lvbiBmb3Jlc3QgYXJlIHRpZ2h0bHkgY2x1c3RlcmVkIGFyb3VuZCBgbWVhbihXKWAuCgpgYGB7cn0KIyBQcm9wZW5zaXR5IG1vZGVsCldfaGF0IDwtIHJlZ3Jlc3Npb25fZm9yZXN0KFgsIFcsIHR1bmUucGFyYW1ldGVycyA9ICJhbGwiKQpXX2hhdC5yZiAgPC0gV19oYXQkcHJlZGljdGlvbnMKCmhpc3QoV19oYXQucmYsIG1haW4gPSAiRXN0aW1hdGVkIHByb3BlbnNpdHkgc2NvcmVzIiwgeGxhYiA9ICJQcm9wZW5zaXR5IFNjb3JlIiwgeGxpbSA9IGMoMCwgMSkpCmBgYAoKPGJyPgoKIyMjIEhldGVyb2dlbmVpdHkgZGV0ZWN0aW9uCgpJIGJlZ2luIGJ5IHRlc3RpbmcgcHJlLXNwZWNpZmllZCBudWxsIGh5cG90aGVzZXMgb2YgdGhlIGZvcm0KCiQkCiAgSF97MH06IFxtYXRob3B7XG1hdGhybXtFfX1bWSgxKSAtIFkoMCkgfCBHX2kgPSAxXSA9IFxtYXRob3B7XG1hdGhybXtFfX1bWSgxKSAtIFkoMCkgfCBHX2kgPSAwXS4KJCQKClRoYXQgaXMsIHRoYXQgdGhlIHRyZWF0bWVudCBlZmZlY3QgaXMgdGhlIHNhbWUgcmVnYXJkbGVzcyBvZiBtZW1iZXJzaGlwIHRvIHRoZSBncm91cCAkR19pJC4gSSBjaG9vc2UgdG8gZXhhbWluZSBob3cgdGhlIG91dGNvbWUgb2YgcGFydGljaXBhdGluZyBpbiB0aGUgZXhwZXJpbWVudCBkaWZmZXJzIGZvciB5b3VuZ2VyIHBlb3BsZSAoYGFnZWx0MzVgID0gMSkuCgpXaGVuIHByZWRpY3RpbmcgdGhlIGNvbmRpdGlvbmFsIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdCAkXHRhdShYX2kpJCBmb3Igb2JzZXJ2YXRpb24gJGkkIHdlIHNob3VsZCBpbiBnZW5lcmFsIGF2b2lkIHVzaW5nIGEgbW9kZWwgdGhhdCB3YXMgZml0dGVkIHVzaW5nIG9ic2VydmF0aW9uICRpJCBUaGlzIHNvcnQgb2Ygc2FtcGxlIHNwbGl0dGluZyAoKipob25lc3R5KiopIGlzIG9uZSBvZiB0aGUgcmVxdWlyZWQgaW5ncmVkaWVudHMgdG8gZ2V0IHVuYmlhc2VkIGVzdGltYXRlcyBvZiB0aGUgQ0FURS4gSSBkbyAzIGZvbGQgY3Jvc3MtZml0dGluZzoKCmBgYHtyfQpZX3RpbGRlID0gYWlwd19HQVRFKFgsVyxZLGVfcmN0LDMpCnN1bW1hcnkobG1fcm9idXN0KFlfdGlsZGUgfiBYWywgJ2FnZWx0MzUnXSkpCmBgYAoKV2Ugb2JzZXJ2ZSB0aGF0IHlvdW5nZXIgcGVvcGxlIG1heSBleHBlcmllbmNlIGFwcHJveGltYXRlbHkgYSAqKjE0JSoqIHJlZHVjdGlvbiBpbiB0aGUgZHVyYXRpb24gb2YgdW5lbXBsb3ltZW50IHNwZWxscyBjb21wYXJlZCB0byB0aGUgYXZlcmFnZSBwcm9ncmFtIGVmZmVjdC4gSG93ZXZlciwgdGhpcyByZXN1bHQgaXMgbm90IHJvYnVzdCBhY3Jvc3MgZGlmZmVyZW50IHNlZWRzIGFuZCBudW1iZXJzIG9mIGZvbGRzLgoKV2l0aCBubyBmdXJ0aGVyIGh5cG90aGVzZXMgdG8gZXhwbG9yZSwgSSBwcm92aWRlIGEgc3VtbWFyeSBvZiB0aGUgdW5kZXJseWluZyBlZmZlY3QgaGV0ZXJvZ2VuZWl0eToKCmBgYHtyfQpzdW1tYXJ5KGxtX3JvYnVzdChZX3RpbGRlIH4gWCkpCmBgYAoKVGhlIGNvZWZmaWNpZW50cyBpbiB0aGlzIHJlZ3Jlc3Npb24gc3VnZ2VzdCBnZW5lcmFsIHRyZW5kcywgYnV0IHRoZXkgc2hvdWxkIG5vdCBiZSBpbnRlcnByZXRlZCBhcyBwYXJ0aWFsIGVmZmVjdHMuIFN1Y2ggaW50ZXJwcmV0YXRpb24gd291bGQgb25seSBiZSBhY2N1cmF0ZSBpZiB0aGUgdHJ1ZSBtb2RlbCB3ZXJlIGdlbnVpbmVseSBsaW5lYXIgaW4gY292YXJpYXRlcyDigJQgYW4gYXNzdW1wdGlvbiB3ZSBnZW5lcmFsbHkgc2hvdWxkbid0IGJlIHdpbGxpbmcgdG8gbWFrZS4KCldoaWxlIHNvbWUgY29lZmZpY2llbnRzIGluIHRoZSBpbnRlcmFjdGl2ZSByZWdyZXNzaW9uIG1heSBhcHBlYXIgc2lnbmlmaWNhbnQsIHRoZXkgY291bGQgYmUgZmFsc2UgcG9zaXRpdmVzLiBBIG1vcmUgYWdncmVnYXRlZCBBSVBXIHJlZ3Jlc3Npb24gaW5kaWNhdGVzIG5vIGxpbmVhciBlZmZlY3QgaGV0ZXJvZ2VuZWl0eS4KCjxicj4KCiMjIDQuIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2FtcGxlCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKSAgCgppbmRpY2VzICAgPC0gc2FtcGxlKDE6bnJvdyhYKSwgc2l6ZSA9IDAuNSpucm93KFgpKQp0cmFpbl9pZHggPC0gbnVtZXJpYyhucm93KFBlbm4pKQoKdHJhaW5faWR4W2luZGljZXNdICA8LSAxICAKClhfdHJhaW4gPSBYW2luZGljZXMsXQpYX3Rlc3QgID0gWFstaW5kaWNlcyxdCgpZX3RyYWluID0gWVtpbmRpY2VzXQpZX3Rlc3QgID0gWVstaW5kaWNlc10KCldfdHJhaW4gPSBXW2luZGljZXNdCldfdGVzdCAgPSBXWy1pbmRpY2VzXQoKIyBTdW1tYXJ5IG9mIHRyZWF0ZWQgYW5kIGNvbnRyb2wgaW4gdHJhaW4gYW5kIHRlc3Qgc2FtcGxlczoKdGFibGUoVHJhaW4gPSBpZmVsc2UoUGVublt0cmFpbl9pZHg9PTEsIF0kdGcgPT0gMCwgIkNvbnRyb2wiLCAiVHJlYXRlZCIpKQp0YWJsZShUZXN0ID0gaWZlbHNlKFBlbm5bdHJhaW5faWR4PT0wLCBdJHRnID09IDAsICJDb250cm9sIiwgIlRyZWF0ZWQiKSkKYGBgCgo8YnI+CgojIyA1LiBBcHBseSBDYXVzYWwgTUwgbWV0aG9kcwoKQmVmb3JlIGFwcGx5aW5nIGFueSBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZHMsIEkgdGhpbmsgaXQgaXMgaW1wb3J0YW50IHRvIGRpc2N1c3MgdGhlIGVzdGltYXRpb24gbWV0aG9kcy4gVGhlIGZvbGxvd2luZyByZWFzb25pbmcgaXMgYmFzZWQgb24gW09rYXNhICgyMDIyKV0oaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzIyMDEuMTI2OTIpLgoKVGhlaXIgc2ltdWxhdGlvbiBldmlkZW5jZSBzdWdnZXN0cyB0aGF0IHVzaW5nIHRoZSBmdWxsIHNhbXBsZSBmb3IgZXN0aW1hdGlvbiBvZiBib3RoIHRoZSBudWlzYW5jZSBmdW5jdGlvbnMgYXMgd2VsbCBhcyB0aGUgQ0FURSBmdW5jdGlvbiBsZWFkcyB0byBhIHJlbWFya2FibHkgZ29vZCBwZXJmb3JtYW5jZSBpbiB0ZXJtcyBvZiBib3RoIGJpYXMgYW5kIHZhcmlhbmNlIGluIGZpbml0ZSBzYW1wbGVzLgoKVGhlIHBvc3NpYmxlIHJlYXNvbiBmb3IgdGhpcyBwaGVub21lbm9uIG1pZ2h0IGJlIGR1ZSB0byB0aGUgb3V0LW9mLWJhZyBwcmVkaWN0aW9ucyBvZiB0aGUgZm9yZXN0IHRoYXQgYXJlIHVzZWQgdGhyb3VnaG91dCB0aGUgc2ltdWxhdGlvbnMuIEV2ZW4gdGhvdWdoIHRoZXNlIHByZWRpY3Rpb25zIGFyZSBub3Qgb3V0LW9mLXNhbXBsZSAqcGVyIHNlKiB0aGV5IGFyZSBub3QgZGlyZWN0bHkgYmFzZWQgb24gdGhlIG9ic2VydmF0aW9ucyB1c2VkIGZvciBlc3RpbWF0aW9uIGFuZCBhcyBzdWNoIG1pZ2h0IGhlbHAgdG8gYWxsZXZpYXRlIHRoZSBvdmVyZml0dGluZyBwcm9ibGVtIHdoZW4gdXNpbmcgZnVsbCBzYW1wbGUuCgpVc2luZyBjcm9zcy1maXR0ZWQgZXN0aW1hdGlvbiwgb25lIHNob3VsZCAodGhlb3JldGljYWxseSkgb2JzZXJ2ZSBhIHNtYWxsZXIgYmlhcyBidXQgaGlnaGVyIHZhcmlhbmNlIG9mIHRoZSBlc3RpbWF0b3JzLiBIb3dldmVyLCBpbiBhbG1vc3QgYWxsIGNhc2VzIGNvbnNpZGVyZWQgaW4gdGhlIHBhcGVyIHRoZSBhdXRob3JzIG9ic2VydmUgYm90aCBoaWdoZXIgYmlhcyBhcyB3ZWxsIGFzIGhpZ2hlciB2YXJpYW5jZSwgcGFydGljdWxhcmx5IGZvciB0aGUgc21hbGwgc2FtcGxlIHNpemVzLgoKQmFzZWQgb24gdGhlIGFib3ZlIHNpbXVsYXRpb24gZXZpZGVuY2UsIGl0IHNlZW1zIHJlYXNvbmFibGUgdG8gYWx3YXlzICoqdXNlIHRoZSBmdWxsLXNhbXBsZSBlc3RpbWF0aW9uIHRvZ2V0aGVyIHdpdGggb3V0LW9mLWJhZyBwcmVkaWN0aW9ucyB3aGVuIGEgcmVsYXRpdmVseSBzbWFsbCBzYW1wbGUgaXMgYXZhaWxhYmxlKiosIHdoZXJlYXMgdG8gdXNlIHRoZSBjcm9zcy1maXR0aW5nIHByb2NlZHVyZSB3aGVuIGEgcmVsYXRpdmVseSBsYXJnZSBkYXRhIGlzIGFjY2Vzc2libGUsICoqcmVnYXJkbGVzcyBvZiB0aGUgY2hvaWNlIG9mIGEgbWV0YS1sZWFybmVyKiouCgpHaXZlbiB0aGF0IHRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBgUGVubmAgZGF0YXNldCBpcyBsaW1pdGVkICgkbj01MjM5JCksIEkgcHJlZmVyIHRvIGRvIHRoZSBmdWxsLXNhbXBsZSBlc3RpbWF0aW9uLgoKPGJyPgoKIyMjIFMtbGVhcm5lcgoKMS4gIFVzZSBNTCBlc3RpbWF0b3IgdG8gZml0IG1vZGVsICRcaGF0e219KFcsWCkkIGluIHRoZSB3aG9sZSB0cmFpbmluZyBzYW1wbGUKCjIuICBFc3RpbWF0ZSBDQVRFIGFzICRcaGF0e1x0YXV9KHgpID0gXGhhdHttfSgxLHgpIC0gXGhhdHttfSgwLHgpJAoKSWYgdGhlIHRyZWF0bWVudCBpbmRpY2F0b3IgaXMgbm90IHN0cm9uZ2x5IHByZWRpY3RpdmUgZm9yIHRoZSBvdXRjb21lLCB0aGUgUy1sZWFybmVyIHdpbGwgdGVuZCB0byBlc3RpbWF0ZSBhIHplcm8gdHJlYXRtZW50IGVmZmVjdC4KClRvIHZlcmlmeSAkVyQncyBsb3cgcHJlZGljdGl2ZSBwb3dlciwgSSBmaXJzdCBlc3RpbWF0ZSB3aGV0aGVyIHBvc3QtbGFzc28gY2hvb3NlcyB0aGUgdHJlYXRtZW50IHZhcmlhYmxlICRXJCB3aGVuIHByZWRpY3RpbmcgJFkkOgoKYGBge3J9CnBvc3RfbGFzc28gPSBoZG06OnJsYXNzbyh4ID0gY2JpbmQoVyxYKSx5ID0gWSkKcG9zdF9sYXNzbyRjb2VmZmljaWVudHNbcG9zdF9sYXNzbyRjb2VmZmljaWVudHMgIT0gMF0KYGBgCgpUaGUgdHJlYXRtZW50ICRXJCBpcyBub3Qgc2VsZWN0ZWQuIEkgdGhlbiBkbyB0aGUgc2ltaWxhciBvcGVyYXRpb24gYnV0IHVzaW5nIGEgZGVjaXNpb24gdHJlZToKCmBgYHtyfQpmaXQgPC0gcnBhcnQoWX4gVytYLGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gMC4wMDE1KSkKcnBhcnQucGxvdChmaXQpCmBgYAoKRm9yIHRoaXMgZGF0YXNldCwgSSBjb25jbHVkZSB0aGF0IHRoZSB0cmVhdG1lbnQgJFckIGlzIG5vdCBhIHN0cm9uZyBwcmVkaWN0b3IgZm9yIHRoZSBvdXRjb21lICRZJCwgd2hpY2ggY291bGQgcG90ZW50aWFsbHkgdW5kZXJtaW5lIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgUy1sZWFybmVyLiBNb3Jlb3ZlciwgYmFzZWQgb24gdGhlIGNvbnRlbXBvcmFyeSBsaXRlcmF0dXJlOgoKMS4gIE9rYXNhICgyMDIyKSBkb2VzIG5vdCByZWNvbW1lbmQgdGhlIHVzYWdlIG9mIHRoZSBTLWxlYXJuZXIgZm9yIGVzdGltYXRpb24gb2YgaGV0ZXJvZ2VuZW91cyBjYXVzYWwgZWZmZWN0cyBkdWUgdG8gZW1waXJpY2FsbHkgZG9jdW1lbnRlZCB1bmRlc2lyYWJsZSBzdGF0aXN0aWNhbCBwcm9wZXJ0aWVzIHN1Y2ggYXMgaGlnaCBiaWFzIGFuZCBjb25zaXN0ZW5jeSBpc3N1ZXMuCjIuICBBbGFhIGFuZCB2YW4gZGVyIFNjaGFhciAoMjAxOCkgc2ltaWxhcmx5IGZpbmQgdGhlIFQtbGVhcm5lciB0byB1bmlmb3JtbHkgb3V0cGVyZm9ybSB0aGUgUy1sZWFybmVyIGluIGVzdGltYXRpbmcgaGV0ZXJvZ2VuZW91cyB0cmVhdG1lbnQgZWZmZWN0cy4KMy4gIFJlbGF0ZWQgc2ltdWxhdGlvbiBzdHVkaWVzIChKYWNvYiwgMjAyMDsgS25hdXMgZXQgYWwuLCAyMDIxKSBmaW5kIGFsc28gYSByZWxhdGl2ZWx5IGNvbXBldGl0aXZlIHBlcmZvcm1hbmNlIG9mIHRoZSBULWxlYXJuZXIsIGVzcGVjaWFsbHkgd2l0aCB0aGUgUmFuZG9tIEZvcmVzdCBhcyBhIGJhc2UgbGVhcm5lci4KCkhlbmNlLCBJIGhhdmUgZGVjaWRlZCB0byBleGNsdWRlIHRoaXMgbWV0aG9kIGR1ZSB0byB1bmZhdm9yYWJsZSBlc3RpbWF0aW9uIGNvbmRpdGlvbnMuCgo8YnI+CgojIyMgVC1MZWFybmVyCgpGb3IgdGhlIFQtbGVhcm5lciwgSSBpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBwcm9jZWR1cmU6CgoxLiAgVXNlIHJlZ3Jlc3Npb24gZm9yZXN0IHRvIGZpdCBtb2RlbCAkXGhhdHttfSgxLFgpJCBpbiB0cmVhdGVkCgoyLiAgVXNlIHJlZ3Jlc3Npb24gZm9yZXN0IHRvIGZpdCBtb2RlbCAkXGhhdHttfSgwLFgpJCBpbiBjb250cm9scwoKMy4gIEVzdGltYXRlIENBVEUgYXMgJFxoYXR7XHRhdX0oeCkgPSBcaGF0e219KDEseCkgLSBcaGF0e219KDAseCkkCgpUaGlzIHByb2NlZHVyZSBpcyBleHBlY3RlZCB0byB3b3JrIHBhcnRpY3VsYXJseSB3ZWxsIGlmIHRoZSBDQVRFIGZ1bmN0aW9uIGlzIGNvbXBsaWNhdGVkIGFuZCB0aGVyZSBhcmUgbm8gY29tbW9uIHRyZW5kcyBpbiB0aGUgcmVzcG9uc2UgZnVuY3Rpb25zLiBJdCBpcyBleHBlY3RlZCB0byB3b3JrIHJhdGhlciBwb29ybHkgaWYgdGhlIENBVEUgZnVuY3Rpb24gaXMgc2ltcGxlLCBhcyB0aGUgcmVzcG9uc2UgZnVuY3Rpb25zIGFyZSBub3QgdHJhaW5lZCBqb2ludGx5IGFuZCB0aGVpciBkaWZmZXJlbmNlIGlzIHVuc3RhYmxlIChPa2FzYSwgMjAyMikuCgpObyBhZGRpdGlvbmFsIHNhbXBsZS1zcGxpdHRpbmcgaW5kdWNlZCBieSBtdWx0aXBsZSBudWlzYW5jZSBmdW5jdGlvbnMgaXMgcmVxdWlyZWQgYXMgdGhlIHJlc3BvbnNlIGZ1bmN0aW9ucyBhcmUgdGhlbXNlbHZlcyBlc3RpbWF0ZWQgb24gc2VwYXJhdGUgc2FtcGxlcyBkZWZpbmVkIGJ5IHRyZWF0ZWQgYW5kIGNvbnRyb2wuCgpgYGB7cn0KY2F0ZV90bCA9IFRfbGVhcm5lcihYLFcsWSx0cmFpbl9pZHgpCmhpc3QoY2F0ZV90bCkKYGBgCgo8YnI+CgojIyMgWC1sZWFybmVyCgpUaGUgcGVyZm9ybWFuY2Ugb2YgdGhlIFQtbGVhcm5lciBpcyBhZ2dyYXZhdGVkIGlmIHRoZSB0cmVhdG1lbnQgYXNzaWdubWVudCBpcyBoaWdobHkgdW5iYWxhbmNlZCwgaS5lLiB3aGVuIHRoZSBtYWpvcml0eSBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIHNhbXBsZSBiZWxvbmcgdG8gb25seSBvbmUgdHJlYXRtZW50IHN0YXR1cy4KClNpbmNlIHRoZXJlIGFyZSBtdWx0aXBsZSB0cmVhdG1lbnQgYXJtcywgdGhlIG51bWJlcnMgb2YgdHJlYXRlZCBhbmQgY29udHJvbCB1bml0cyBhcmUgdW5iYWxhbmNlZCwgd2l0aCAxLDg4NSB0cmVhdGVkIHVuaXRzIGFuZCAzLDM1NCB1bml0cyBpbiBjb250cm9sLiBIZW5jZSwgd2Ugc2hvdWxkIGV4cGVjdCBzb21lIGZvcm0gb2YgcmVndWxhcml6YXRpb24gYmlhcyBmb3IgJFxoYXR7bX0oMSxYKSQuCgpUaGUgZm9sbG93aW5nIGltcGxlbWVudGF0aW9uIGlzIGJhc2VkIG9uIGEgW1Nob3J0IENvdXJzZV0oaHR0cHM6Ly93d3cuZ3NiLnN0YW5mb3JkLmVkdS9mYWN1bHR5LXJlc2VhcmNoL2NlbnRlcnMtaW5pdGlhdGl2ZXMvc2lsL3Jlc2VhcmNoL21ldGhvZHMvYWktbWFjaGluZS1sZWFybmluZy9zaG9ydC1jb3Vyc2UpIGJ5IEF0aGV5LCBTcGllc3MgYW5kIFdhZ2VyLgoKVGhlIGZpcnN0IGVzdGltYXRpb24gc3RlcCBpcyBpZGVudGljYWwgdG8gdGhlIFQtbGVhcm5lci4gSSB1c2UgcmVncmVzc2lvbiBmb3Jlc3QgdG8gZml0IG1vZGVsICRcaGF0e219KDEsWCkkIGluIHRyZWF0ZWQgYW5kICRcaGF0e219KDAsWCkkIGluIGNvbnRyb2w6ICQkXGJlZ2lue2FsaWduKn0KXGhhdHttfV8wKFgpID0gRVtZfCBXPTAsIFhdIFxcClxoYXR7bX1fMShYKSA9IEVbWXwgVz0xLCBYXSBcXApcZW5ke2FsaWduKn0kJAoKRm9yIHRoZSBzZWNvbmQgc3RhZ2UsIEkgKmltcHV0ZSogdGhlIHRyZWF0bWVudCBlZmZlY3QgZm9yIHRoZSBjb250cm9sIGFuZCBmb3IgdGhlIHRyZWF0ZWQgdXNpbmc6ICQkClxiZWdpbnthbGlnbip9ClxoYXR7XHRhdX0oeCwgMCkgJj0gXGhhdHttfV8xKHgsIDApIC0gWSBcXApcaGF0e1x0YXV9KHgsIDEpICY9IFkgLSBcaGF0e219XzAoeCwgMSkgXFxcZW5ke2FsaWduKn0KJCQKCkxhc3RseSwgSSBmaXQgdHdvIG1vcmUgbW9kZWxzIHRvIHByZWRpY3QgdGhlc2UgZWZmZWN0cy4gV2l0aCAkXGhhdHtlfSh4KSQgYmVpbmcgdGhlIHByb3BlbnNpdHkgc2NvcmUgbW9kZWwsIG9uZSBjYW4gY29tYmluZSB0aGUgdHdvIHNlY29uZCBzdGFnZSBtb2RlbHMgYXMgZm9sbG93czokJFxiZWdpbnthbGlnbip9ClxoYXR7XHRhdX0oeCkgJj0gXGhhdHtcdGF1fSh4LCAwKSBcaGF0e2V9KHgpICsgXGhhdHtcdGF1fSh4LCAxKSAoMS1caGF0e2V9KHgpKQpcZW5ke2FsaWduKn0kJAoKWC1sZWFybmVyIGlzIGV4cGVjdGVkIHRvIHdvcmsgcGFydGljdWxhcmx5IHdlbGwgaW4gdW5iYWxhbmNlZCBzZXR0aW5ncywgd2hpY2ggaXMgb2Z0ZW4gdGhlIGNhc2UgaW4gcHJhY3RpY2UgYXMgdGhlIHNoYXJlIG9mIHRyZWF0ZWQgbWlnaHQgYmUgcmVzdHJpY3RlZCBmaW5hbmNpYWxseSBvciBvdGhlcndpc2UgKE9rYXNhLCAyMDIyKS4KCk1vcmVvdmVyLCB0aGUgWC1sZWFybmVyIG1pZ2h0IGJlIGxlc3MgcHJvbmUgdG8gb3ZlcmZpdHRpbmcgYmlhcyB3aGljaCB3b3VsZCBwYXJ0bHkganVzdGlmeSB0aGUgZnVsbC1zYW1wbGUgZXN0aW1hdGlvbiBhcyBkZXNjcmliZWQgaW4gS8O8bnplbCBldCBhbC4gKDIwMTkpLgoKYGBge3J9CmNhdGVfeGwgPSBYX2xlYXJuZXIoWCxXLFksdHJhaW5faWR4LGVfcmN0KQpoaXN0KGNhdGVfeGwpCmBgYAoKPGJyPgoKIyMjIENhdXNhbCBGb3Jlc3QKClRoZSBjYXVzYWwgZm9yZXN0IGVzdGltYXRlcyBDQVRFcyB1c2luZyBhICpsb2NhbGl6ZWQgdmVyc2lvbiBvZiB0aGUgcGFydGlhbGx5IGxpbmVhciBlc3RpbWF0b3IqOgoKJCRcYmVnaW57ZXF1YXRpb259CiAgICBcaGF0e1x0YXV9XntjZn0oeCkgPSBhcmdtaW5fe1x0YXV9IFxsZWZ0XHsgXHN1bV97aT0xfV5OIFxhbHBoYV9pKHgpIFxsZWZ0WyhZX2kgLSBcaGF0e219KFhfaSkpICAtIFx0YXUoeCkoV19pIC0gXGhhdHtlfShYX2kpKSBccmlnaHRdXjIgXHJpZ2h0XH0sClxlbmR7ZXF1YXRpb259CiQkIHdoZXJlICRcYWxwaGEoeCkkIGFyZSAkeCQtc3BlY2lmaWMgd2VpZ2h0cy4KClVzaW5nIGBwcmVkaWN0YCBmdW5jdGlvbiwgSSBnZXQgb3V0LW9mLWJhZyBlc3RpbWF0ZWQgaW5kaXZpZHVhbCBBVEVzIGZvciBldmVyeSBpbmRpdmlkdWFsIGluIHRoZSBzYW1wbGU6CgpgYGB7cn0KY2YgPSBjYXVzYWxfZm9yZXN0KFhfdHJhaW4sIFlfdHJhaW4sIFdfdHJhaW4sIFcuaGF0ID0gZV9yY3QpCmNhdGVfY2YgPSBwcmVkaWN0KGNmLFhfdGVzdCkkcHJlZGljdGlvbnMKaGlzdChjYXRlX2NmKQpgYGAKClRoZSBgZ3JmYCBwYWNrYWdlIGFsc28gcHJvZHVjZXMgYSBtZWFzdXJlIG9mIHZhcmlhYmxlIGltcG9ydGFuY2UgdGhhdCBpbmRpY2F0ZXMgaG93IG9mdGVuIGEgdmFyaWFibGUgd2FzIHVzZWQgaW4gYSB0cmVlIHNwbGl0LgoKVGhpcyBjYW4gYmUgYSByb3VnaCBkaWFnbm9zdGljLCBidXQgaXQgc2hvdWxkIG5vdCBiZSBpbnRlcnByZXRlZCBhcyBpbmRpY2F0aW5nIHRoYXQsIGZvciBleGFtcGxlLCB2YXJpYWJsZSB3aXRoIGxvdyBpbXBvcnRhbmNlIGlzIG5vdCByZWxhdGVkIHRvIGhldGVyb2dlbmVpdHk6IGlmIHR3byBjb3ZhcmlhdGVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCwgdGhlIHRyZWVzIG1pZ2h0IHNwbGl0IG9uIG9uZSBjb3ZhcmlhdGUgYnV0IG5vdCB0aGUgb3RoZXIsIGV2ZW4gdGhvdWdoIGJvdGggKG9yIG1heWJlIG5laXRoZXIpIGFyZSByZWxldmFudCBpbiB0aGUgdHJ1ZSBER1AuCgpgYGB7cn0KY292YXJpYXRlcyA8LSBjKCJmZW1hbGUiLCAiYmxhY2siLCAib3RocmFjZSIsICJmYWN0b3IoZGVwKTEiLCAiZmFjdG9yKGRlcCkyIiwKICAgICAgICAgICAgICAgICAgICAgInEyIiwgInEzIiwgInE0IiwgInE1IiwgInE2IiwgImFnZWx0MzUiLCAiYWdlZ3Q1NCIsICJkdXJhYmxlIiwKICAgICAgICAgICAgICAgICAgICAgImx1c2QiLCAiaHVzZCIpCgp2YXJfaW1wICAgICAgICA8LSBjKHZhcmlhYmxlX2ltcG9ydGFuY2UoY2YpKQpuYW1lcyh2YXJfaW1wKSA8LSBjb3ZhcmlhdGVzCnNvcnRlZF92YXJfaW1wIDwtIHNvcnQodmFyX2ltcCwgZGVjcmVhc2luZyA9IFRSVUUpCgpmb3IgKGkgaW4gMTo1KSB7Y2F0KHNwcmludGYoIiUtMTVzICUtMTAuNGZcbiIsIG5hbWVzKHNvcnRlZF92YXJfaW1wKVtpXSwgc29ydGVkX3Zhcl9pbXBbaV0pKX0KYGBgCgo8YnI+CgojIyMgRFItbGVhcm5lcgoKVGhlIERSLWxlYXJuZXIgY3JlYXRlcyB0aGUgcHNldWRvLW91dGNvbWUgaW4gYSBmaXJzdCBzdGVwICQkCiAgIFxiZWdpbnthbGlnbn0KICAgICAgICAgXHRpbGRle1l9X3tBVEV9ID0gXHVuZGVyYnJhY2V7XGhhdHttfSgxLFgpIC0gXGhhdHttfSgwLFgpfV97XHRleHR7b3V0Y29tZSBwcmVkaWN0aW9uc319ICsgXHVuZGVyYnJhY2V7XGZyYWN7VyAoWSAtIFxoYXR7bX0oMSxYKSl9e1xoYXR7ZX0oWCl9IC0gXGZyYWN7KDEtVykgKFkgLSBcaGF0e219KDAsWCkpfXsxLVxoYXR7ZX0oWCl9fV97XHRleHR7d2VpZ2h0ZWQgcmVzaWR1YWxzfX0gXG5vbnVtYmVyCiAgICBcZW5ke2FsaWdufSQkYW5kIHVzZXMgaXQgaW4gYSBmaW5hbCByZWdyZXNzaW9uIHN0ZXAgKGEgZ2VuZXJpYyBNTCBwcm9ibGVtKS4KCkRSLWxlYXJuZXIgaXMgZXhwZWN0ZWQgdG8gd29yayB3ZWxsIGluIHNpbWlsYXIgc2l0dWF0aW9ucyBhcyB0aGUgWC1sZWFybmVyIHdpdGggYSBtb3JlIGJhbGFuY2VkIHRyZWF0bWVudCBhc3NpZ25tZW50LCBhcyB0b28gZXh0cmVtZSBwcm9wZW5zaXR5IHNjb3JlcyBtaWdodCB5aWVsZCB0aGUgZXN0aW1hdG9yIHVuc3RhYmxlIChub3QgYW4gaXNzdWUgZm9yIFBlbm4pLiBJdCBzaG91bGQgaGF2ZSBhbiBhZGRpdGlvbmFsIGFkdmFudGFnZSBvdmVyIHRoZSBYLWxlYXJuZXIgZHVlIHRvIGl0cyBkb3VibGUgcm9idXN0bmVzcyBwcm9wZXJ0eS4KCi0gICBUaGUgc2ltdWxhdGlvbnMgb2YgS2VubmVkeSAoMjAyMCkgc3VnZ2VzdCBhIGZhc3RlciBjb252ZXJnZW5jZSByYXRlIG9mIHRoZSBEUi1sZWFybmVyIGluIGNvbXBhcmlzb24gdG8gdGhlIFgtIGFuZCBULWxlYXJuZXIuCgotICAgRm9yIHRoZSBzbWFsbGVyIHNhbXBsZSBzaXplcyAoNTAwIGFuZCAy4oCyMDAwKSwgdGhlIHJlZHVjdGlvbiBpbiB0aGUgb3ZlcmZpdHRpbmcgYmlhcyBpcyBub3QgbGFyZ2UgZW5vdWdoIGluIGNvbXBhcmlzb24gdG8gdGhlIGJpYXMgc3RlbW1pbmcgZnJvbSB0aGUgZXN0aW1hdGlvbiBvZiB0aGUgQ0FURSBmdW5jdGlvbi4gRm9yIHNtYWxsIHNhbXBsZSBzaXplcyB0aGUgYWRkaXRpb25hbCBzcGxpdHRpbmcgb2YgdGhlIHNhbXBsZSBkb2VzIG5vdCBsZWF2ZSBlbm91Z2ggb2JzZXJ2YXRpb25zIHRvIGxlYXJuIHRoZSBub24tbGluZWFyIHN0cnVjdHVyZSBvZiB0aGUgQ0FURSAoT2thc2EgMjAyMikuCgpJdCBpcyB3ZWxsLXN1aXRlZCBmb3IgdGhlIG51aXNhbmNlIHBhcmFtZXRlcnMsIGJ1dCBzaG91bGQgaGF2ZSBhIGhhcmRlciB0aW1lIHdpdGggdGhlIGxpbmVhciBoZXRlcm9nZW5lb3VzIGVmZmVjdHM6CgpgYGB7cn0KY2F0ZV9kciA9IERSX2xlYXJuZXIoWCxXLFksdHJhaW5faWR4LGVfcmN0KQpoaXN0KGNhdGVfZHIpCmBgYAoKPGJyPgoKIyMjIFItbGVhcm5lcgoKVGhlIGdlbmVyaWMgdmVyc2lvbiBvZiBSLWxlYXJuZXIgaXMgdG8gdXNlIHRoZSB1bm1vZGlmaWVkIGNvdmFyaWF0ZXMgaW4gYSB3ZWlnaHRlZCByZWdyZXNzaW9uIHdpdGggcHNldWRvIG91dGNvbWVzOiAkJFxoYXR7XHRhdX1ee3JsfSh4KSAgPSBhcmdtaW5fe1x0YXV9IFxzdW1fe2k9MX1eTiBcdW5kZXJicmFjZXsoV19pIC0gXGhhdHtlfShYX2kpKV4yfV97XHRleHR7d2VpZ2h0fX0gXGxlZnQoXHVuZGVyYnJhY2V7XGZyYWN7WV9pIC0gXGhhdHttfShYX2kpfXtXX2kgLSBcaGF0e2V9KFhfaSl9fV97XHRleHR7cHNldWRvLW91dGNvbWV9fSAtICBYX2lcYmV0YSBccmlnaHQpXjIkJAoKUi1sZWFybmVyIGlzIGV4cGVjdGVkIHRvIHdvcmsgaW4gYSBzZXR0aW5ncyB3aGVyZSB0aGUgbnVpc2FuY2UgZnVuY3Rpb25zIGFzIHdlbGwgYXMgdGhlIENBVEUgZnVuY3Rpb24gbWlnaHQgaGF2ZSBhIGhpZ2ggZGVncmVlIG9mIGNvbXBsZXhpdHkuIE5pZSBhbmQgV2FnZXIgKDIwMjEpIHNob3cgZ29vZCBwZXJmb3JtYW5jZSBvZiB0aGUgUi1sZWFybmVyIGluIHNldHRpbmdzIHdpdGggY29tcGxpY2F0ZWQgbnVpc2FuY2UgZnVuY3Rpb25zIGFuZCByYXRoZXIgc2ltcGxlIENBVEUgZnVuY3Rpb24uCgpUaGUgcGVyZm9ybWFuY2Ugb2YgdGhlIFItbGVhcm5lciBpcyBjb21wZXRpdGl2ZSB3aXRoIHRoZSBvdGhlciBtZXRhLWxlYXJuZXJzIGVzcGVjaWFsbHkgaW4gc21hbGxlciBzYW1wbGVzLCBwYXJ0aWN1bGFybHkgZm9yIHRoZSBmdWxsLXNhbXBsZSB2ZXJzaW9uIChPa2FzYSwgMjAyMikuCgpgYGB7cn0KY2F0ZV9ybF9yZiA9IFJfbGVhcm5lcihYLFcsWSx0cmFpbl9pZHgsZV9yY3QpCmhpc3QoY2F0ZV9ybF9yZikKYGBgCgo8YnI+CgojIyA2LiBQcmVkaWN0IHRoZSBDQVRFcyBpbiB0aGUgdGVzdCBzYW1wbGUKCk5vdywgbGV0J3MgY29tcGFyZSB0aGUgZXN0aW1hdGVkIGVmZmVjdHMgb2YgdGhlIGRpZmZlcmVudCBtZXRob2RzIHVzZWQgc28gZmFyOgoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYgPC0gZGF0YS5mcmFtZSgKICBUX2xlYXJuZXIgPSBjYXRlX3RsLAogIFhfbGVhcm5lciA9IGNhdGVfeGwsCiAgUl9sZWFybmVyID0gY2F0ZV9ybF9yZiwKICBEUl9sZWFybmVyID0gY2F0ZV9kciwKICBDYXVzYWxfRm9yZXN0ID0gY2F0ZV9jZikKCmRmX2xvbmcgPC0gdGlkeXI6OmdhdGhlcihkZiwga2V5ID0gIk1ldGhvZCIsIHZhbHVlID0gIkNBVEUiKQoKZ2dwbG90KGRmX2xvbmcsIGFlcyh4ID0gQ0FURSwgZmlsbCA9IE1ldGhvZCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBsYWJzKHRpdGxlID0gIkRlbnNpdGllcyBvZiBFc3RpbWF0ZWQgQ0FURSIsCiAgICAgICB4ID0gIkNvbmRpdGlvbmFsIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCAoQ0FURSkiLAogICAgICAgeSA9ICJEZW5zaXR5IikgKyB0aGVtZV9taW5pbWFsKCkgKyB4bGltKGMoLTEuNSwgMS41KSkgIyArIHlsaW0oYygwLDMuMikpCmBgYAoKYGBge3J9CnJlc3VsdHMgPSBjYmluZChjYXRlX3RsLGNhdGVfeGwsY2F0ZV9jZixjYXRlX2RyLGNhdGVfcmxfcmYpCmNvbG5hbWVzKHJlc3VsdHMpID0gYygiVC1sZWFybmVyIiwiWC1sZWFybmVyIiwiQ2F1c2FsIEZvcmVzdCIsIkRSLWxlYXJuZXIiLCJSLWxlYXJuZXIiKQpwYWlycy5wYW5lbHMocmVzdWx0cyxtZXRob2QgPSAicGVhcnNvbiIpCmRlc2NyaWJlKHJlc3VsdHMpCmBgYAoKQWxsIG1ldGhvZHMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLiBEUiBhbmQgUi1sZWFybmVycyBhcmUgbW9yZSBzcHJlYWQgb3V0IGluIHRoZSBwcmVkaWN0ZWQgQ0FURS4gQXQgZmlyc3QgZ2xhbmNlLCB0aGUgcmVzdWx0cyBsb29rIHByb21pc2luZywgd2l0aG91dCB1bmV4cGVjdGVkIGdsaXRjaGVzLgoKPGJyPgoKIyMgNy4gRXZhbHVhdGluZyBIZXRlcm9nZW5lb3VzIEVmZmVjdHMKCk9uZSBtaWdodCBiZSB0ZW1wdGVkIHRvIHRoaW5rOiDigJxpZiB0aGUgaGlzdG9ncmFtIGlzIGNvbmNlbnRyYXRlZCBhdCBhIHBvaW50LCB0aGVuIHRoZXJlIGlzIG5vIGhldGVyb2dlbmVpdHk7IGlmIHRoZSBoaXN0b2dyYW0gaXMgc3ByZWFkIG91dCwgdGhlbiBvdXIgZXN0aW1hdG9yIGhhcyBmb3VuZCBpbnRlcmVzdGluZyBoZXRlcm9nZW5laXR5LuKAnSBIb3dldmVyLCB0aGlzIG1heSBiZSBbZmFsc2VdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3N0YW5mb3JkZ3Nic2lsYWIvbWwtY2ktdHV0b3JpYWwvaHRlLWktYmluYXJ5LXRyZWF0bWVudC5odG1sI3ZpYS1ncmYpLgoKSWYgdGhlIGhpc3RvZ3JhbSBpcyBjb25jZW50cmF0ZWQgYXQgYSBwb2ludCwgd2UgbWF5IGJlIHVuZGVyLXBvd2VyZWQ6IG91ciBtZXRob2Qgd2FzIG5vdCBhYmxlIHRvIGRldGVjdCBhbnkgaGV0ZXJvZ2VuZWl0eSwgYnV0IG1heWJlIGl0IHdvdWxkIGRldGVjdCBpdCBpZiB3ZSBoYWQgbW9yZSBkYXRhLiBJZiB0aGUgaGlzdG9ncmFtIGlzIHNwcmVhZCBvdXQsIHdlIG1heSBiZSBvdmVyZml0dGluZzogb3VyIG1vZGVsIGlzIHByb2R1Y2luZyB2ZXJ5IG5vaXN5IGVzdGltYXRlcyAkXGhhdHtcdGF1fSh4KSQsIGJ1dCBpbiBmYWN0IHRoZSB0cnVlICR7XHRhdX0oeCkkIGNhbiBiZSBtdWNoIHNtb290aGVyIGFzIGEgZnVuY3Rpb24gb2YgJHgkLgoKVGhlIGdvYWwgaXMgdG8gdHJ5IHRvIGFzc2VzcyB0aGUgdmFsaWRpdHkgb2YgdGhlIGVzdGltYXRlZCBDQVRFcy4KCjxicj4KCiMjIyBCeSBoYW5kOiBCZXN0IExpbmVhciBQcmVkaWN0b3IKCioqU3RyYXRlZ3kgQToqKiBXZWlnaHRlZCByZXNpZHVhbCBCTFAKCiQkKFxiZXRhXzEsIFxiZXRhXzIpID0gYXJnbWluX3tiXzEsYl8yfUVce1xvbWVnYShYKVtZIC0gYl8xIChXIC0gZShYKSkgIC0gYl8yIChXIC0gZShYKSkgKFxoYXR7XHRhdX0oWCkgLSBFW1xoYXR7XHRhdX0oWCldKSAtIGEgXHRpbGRle1h9XV4yXH0gJCQKCndoZXJlICRcb21lZ2EoWCkgPSBbZShYKSAoMS1lKFgpKV1eey0xfSQuCgoqKlN0cmF0ZWd5IEIqKiBIb3J2aXR6LVRob21wc29uIEJMUCQkKFxiZXRhXzEsIFxiZXRhXzIpID0gYXJnbWluX3tiXzEsYl8yfUVce1tIWSAtIGJfMSAgLSBiXzIgKFxoYXR7XHRhdX0oWCkgLSBFW1xoYXR7XHRhdX0oWCldKSAgLSBhIEhcdGlsZGV7WH1dXjJcfSAkJAoKd2hlcmUgJEggPSBcZnJhY3tXLWUoWCl9e2UoWCkoMS1lKFgpKX0kLiBUaGUgZGUtbm9pc2luZyBjb21wb25lbnQgJFx0aWxkZXtYfSQgaW4gYm90aCBzdHJhdGVnaWVzIGlzICRcaGF0e219KDAsWCkkIG9idGFpbmVkIGJ5IHJlZ3Jlc3Npb24gZm9yZXN0LgoKPiAkXGJldGFfMiA9IDAkICppZiBlaXRoZXIgdGhlIGVmZmVjdHMgYXJlIGhvbW9nZW5lb3VzIG9yIG91ciBDQVRFIGVzdGltYXRlcyBhcmUg4oCcYmFkLuKAnSBUaHVzLCByZWplY3RpbmcgdGhlIGh5cG90aGVzaXMqICRcYmV0YV8yID0gMCQgKndvdWxkIGltcGx5IHRoYXQgYm90aCB0aGUgZWZmZWN0cyBhcmUgaGV0ZXJvZ2VuZW91cyBhbmQgb3VyIENBVEUgZXN0aW1hdGVzIGFyZSByZWxpYWJsZSogLSBbZXZhbHVDQVRFIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vcmljY2FyZG8tZGYuZ2l0aHViLmlvL2V2YWx1Q0FURS9hcnRpY2xlcy9ldmFsdUNBVEUtc2hvcnQtdHV0b3JpYWwuaHRtbCNmbjIpCgpUbyBjb21wYXJlIGFsbCBtZXRob2RzLCBJIGJlZ2luIGJ5IHVzaW5nIGEgbW9kaWZpZWQgZnVuY3Rpb24gaW50cm9kdWNlZCBpbiB0aGUgYEV4ZXJjaXNlIDhgLiBUaGlzIGZ1bmN0aW9uIHRha2VzIHByZWRpY3RlZCBDQVRFLCB0ZXN0IG91dGNvbWVzLCB0ZXN0IHRyZWF0bWVudCwgYW5kIGRlLW5vaXNpbmcgdmFyaWFibGVzIGFzIGlucHV0cywgcHJvZHVjaW5nIHRoZSBlc3RpbWF0ZWQgJFxiZXRhXzIkIGNvZWZmaWNpZW50cyBhbmQgdGhlaXIgc3RhbmRhcmQgZXJyb3JzIGFzIG91dHB1dHMuCgpgYGB7cn0KIyBEZW5vaXNpbmcgCnJmbTAgICAgID0gcmVncmVzc2lvbl9mb3Jlc3QoWF90cmFpbltXX3RyYWluPT0wLF0sWV90cmFpbltXX3RyYWluPT0wXSkKbWhhdDBfcmYgPSBwcmVkaWN0KHJmbTAsIFhfdGVzdCkkcHJlZGljdGlvbnMKYGBgCgpgYGB7cn0KQkxQX3RsID0gQkxQX2xlY3R1cmUoY2F0ZV90bCxZX3Rlc3QsV190ZXN0LG1oYXQwX3JmLCJULWxlYXJuZXIgUkYiKQpCTFBfeGwgPSBCTFBfbGVjdHVyZShjYXRlX3hsLFlfdGVzdCxXX3Rlc3QsbWhhdDBfcmYsIlgtbGVhcm5lciBSRiIpCkJMUF9jZiA9IEJMUF9sZWN0dXJlKGNhdGVfY2YsWV90ZXN0LFdfdGVzdCxtaGF0MF9yZiwiQ2F1c2FsIEZvcmVzdCIpCkJMUF9kciA9IEJMUF9sZWN0dXJlKGNhdGVfZHIsWV90ZXN0LFdfdGVzdCxtaGF0MF9yZiwiRFItbGVhcm5lciIpCkJMUF9ybCA9IEJMUF9sZWN0dXJlKGNhdGVfcmxfcmYsWV90ZXN0LFdfdGVzdCxtaGF0MF9yZiwiUi1sZWFybmVyIikKCnJlc3VsdHNfQkxQID0gbGlzdChCTFBfdGwsQkxQX3hsLEJMUF9jZixCTFBfZHIsQkxQX3JsKQpuYW1lcyAgICAgICA9IGMoIlQtbGVhcm5lciIsIlgtbGVhcm5lciIsIkNhdXNhbCBGb3Jlc3QiLCJEUi1sZWFybmVyIiwiUi1sZWFybmVyIikKCnBsb3RfQkxQKHJlc3VsdHNfQkxQLCBuYW1lcykKYGBgCgpJdCBpcyB3b3J0aCBtZW50aW9uaW5nIHRoYXQgZGUtbm9pc2luZyBoZWxwcyB0byBnZXQgbW9yZSBwcmVjaXNlIGVzdGltYXRlcy4gTm9uZXRoZWxlc3MsIHRoZSByZXN1bHRzIGFyZSBxdWl0ZSBtaXNlcmFibGUuIENhdXNhbCBGb3Jlc3QgcHJlZGljdGlvbnMgc2VlbSB0byBiZSB0aGUgYmVzdCBjYW5kaWRhdGUgZm9yIHRoZSBgZXZhbHVDQVRFYC4KCjxicj4KCiMjIyBVc2luZyBgZXZhbHVDQVRFYCBwYWNrYWdlCgo+ICoiSXQncyBsaWtlIHB1dHRpbmcgdGhlIHJlYWwgdGhpbmcgb24gdGhlIGxlZnQgaGFuZCBzaWRlLCBidXQgd2UgZG9uJ3Qgb2JzZXJ2ZSB0aGUgcmVhbCB0aGluZywgYnV0IGNhbiBhcHByb3hpbWF0ZSBpdCB1c2luZyBhbiB1bmJpYXNlZCBzaWduYWwqICRIWSQqLiBJZiBvdXIgcHJlZGljdGlvbnMgY29ycmVsYXRlIHdpdGggdGhpcyB1bmJpYXNlZCBzaWduYWwsIHRoZW4gd2UgZGlkIGEgZ29vZCBqb2IiKiwgLSBNLkMuIEtuYXVzCgpgYGB7cn0KIyBGdWxsIHNhbXBsZSAodHJhaW4rdGVzdCkgcHJlZGljdGlvbjogCmV2YWxfY2F0ZV9jZiA9IHByZWRpY3QoY2YsWCkkcHJlZGljdGlvbnMKCiMgTG9naWNhbCB0cmVhdG1lbnQgaW5kaWNhdG9yOiAKdHJhaW5faWR4X2wgID0gYXMubG9naWNhbCh0cmFpbl9pZHgpCgpldmFsdWF0aW9uIDwtIGV2YWx1Q0FURShZLCBXLCBYLCBldmFsX2NhdGVfY2YsIHRyYWluX2lkeF9sLCBwc2NvcmUgPSByZXAoZV9yY3QsIGxlbmd0aChZKSkpCnN1bW1hcnkoZXZhbHVhdGlvbiwgdGFyZ2V0ID0gIkJMUCIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBXaGF0IGEgd29uZGVyZnVsIHBsb3QgYnkgQ2hhdEdQVCEgCmVzdGltYXRlcyA8LSBjKDAuNzQsIDAuNzIsIDAuNjgsIDAuNzYsIDAuNzEsIDAuNjksIDAuNzIsIDAuNzEsIDAuNzUpCmxvd2VyX2NpIDwtIGMoIDAuMTA4LCAwLjA4NSwgMC4wNDQsIDAuMTA4LCAwLjA4MiwgMC4wNjEsIDAuMDc5LCAwLjA2NSwgMC4wNjEpCnVwcGVyX2NpIDwtIGMoMS4zNzksIDEuMzUsIDEuMzI0LCAxLjQxMiwgMS4zNDcsIDEuMzI5LCAxLjM1MywgMS4zNTYsIDEuNDM4KQptZXRob2RzIDwtIGMoICJ3cl9jZGRmMSIsICJ3cl9jZGRmMiIsICJ3cl9tY2sxIiwgImh0X2NkZGYxIiwgImh0X2NkZGYyIiwgImh0X21jazEiLCAiaHRfbWNrMiIsICJodF9tY2szIiwgImFpcHciKQoKcGxvdF9IVEUgPC0gZGF0YS5mcmFtZShNZXRob2QgPSBtZXRob2RzLCBFc3RpbWF0ZSA9IGVzdGltYXRlcywgTG93ZXJDSSA9IGxvd2VyX2NpLCBVcHBlckNJID0gdXBwZXJfY2kpCgoKZ2dwbG90KHBsb3RfSFRFLCBhZXMoeCA9IE1ldGhvZCwgeSA9IEVzdGltYXRlLCBmaWxsID0gTWV0aG9kKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gTG93ZXJDSSwgeW1heCA9IFVwcGVyQ0kpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0gMC4yNSkgKwogIGxhYnModGl0bGUgPSAiRXN0aW1hdGVkIEhFVCArIDk1JSBDb25maWRlbmNlIEludGVydmFscyIsCiAgICAgICB5ID0gIkVzdGltYXRlIiwKICAgICAgIHggPSAiTWV0aG9kIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIpCmBgYAoKVGhlIHJlc3VsdHMgYXJlIGFzIGZvbGxvd3M6CgotICAgYEFURWAgJFxhcHByb3ggLSAwLjA1JCwgYnV0IG5ldmVyIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgotICAgYEhURWAgJFxhcHByb3ggMC43JCwgYW5kIHZlcnkgbm9pc3kuIFdlIGFyZSBwYXJ0aWFsbHkgYWJsZSB0byBkZXRlY3QgdGhlIGhldGVyb2dlbmVpdHkgKGFuZCBwcm9iYWJseSBzbGlnaHRseSBvdmVyZml0IHRoZSBDQVRFIGZ1bmN0aW9uOiAkXGJldGFfMiA8IDEkKS4KClRoZSBzbG9wZSAkXGJldGFfMiQgaXMgYSBtZWFzdXJlIG9mIGhvdyB0aGUgQ0FURSBwcmVkaWN0aW9ucyBjby12YXJ5IHdpdGggdGhlIHRydWUgQ0FURS4gVGhlcmVmb3JlLCB0aGUgcC12YWx1ZSBvbiB0aGUgZXN0aW1hdGUgb2YgdGhlIGNvZWZmaWNpZW50IGFsc28gYWN0cyBhcyBhbiBvbW5pYnVzIHRlc3QgZm9yIHRoZSBwcmVzZW5jZSBvZiBoZXRlcm9nZW5laXR5LiBJZiB0aGUgY29lZmZpY2llbnQgaXMgc2lnbmlmaWNhbnRseSBncmVhdGVyIHRoYW4gemVybywgdGhlbiB3ZSBjYW4gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgb2Ygbm8gaGV0ZXJvZ2VuZWl0eS4KCkhvd2V2ZXIsIGNvZWZmaWNpZW50cyBzbWFsbGVyIHRoYW4gMCBhcmUgbm90IG1lYW5pbmdmdWwgYW5kIHNob3VsZCBub3QgYmUgaW50ZXJwcmV0ZWQgKFtzb3VyY2VdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3N0YW5mb3JkZ3Nic2lsYWIvbWwtY2ktdHV0b3JpYWwvaHRlLWktYmluYXJ5LXRyZWF0bWVudC5odG1sI2Jlc3QtbGluZWFyLXByb2plY3Rpb24pKS4KCjxicj4KCiMjIyBHQVRFUwoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGVzdGltYXRpb24gb2YgdGhlIEdBVEVTIGZvciB0d28gbWFpbiBbcmVhc29uc10oaHR0cHM6Ly9yaWNjYXJkby1kZi5naXRodWIuaW8vZXZhbHVDQVRFL2FydGljbGVzL2V2YWx1Q0FURS1zaG9ydC10dXRvcmlhbC5odG1sKToKCi0gICBUaGV5IHF1YW50aWZ5IHRoZSBleHRlbnQgdG8gd2hpY2ggdGhlIGVmZmVjdHMgZGlmZmVyIGFjcm9zcyBncm91cHM7CgotICAgVGhleSBhbGxvdyB0byBhc3Nlc3Mgd2hldGhlciB3ZSBkZXRlY3Qgc3lzdGVtYXRpYyBoZXRlcm9nZW5laXR5IG9yIGp1c3QgZXN0aW1hdGlvbiBub2lzZSBieSB0ZXN0aW5nIGEgc2V0IG9mIGh5cG90aGVzZXMuCgpJZiB0aGUgc2xpY2VzIGFyZSBidWlsdCBvbiB0aGUgdHJ1ZSBDQVRFLCB3ZSB3b3VsZCBvYnNlcnZlIHRoZSBmb2xsb3dpbmcgbW9ub3RvbmljaXR5OiQkIFxnYW1tYV8xXiogXGxlcSBcbGRvdHMgXGxlcSBcZ2FtbWFfS14qICQkd2hlcmUgdGhlICReKiQgaW5kaWNhdGVzIHRoYXQgdGhlIHBhcmFtZXRlciBidWlsZHMgb24gdGhlIHRydWUgQ0FURS4gVGhlcmVmb3JlLCB3ZSBleHBlY3QgdG8gc2VlIHRoZSBzYW1lIG1vbm90b25pY2l0eSBpZiAkXGhhdHtcdGF1fShYKSQgcHJvdmlkZXMgYSBnb29kIGFwcHJveGltYXRpb24gb2YgJFx0YXUoWCkkLgoKYGBge3J9CnN1bW1hcnkoZXZhbHVhdGlvbiwgdGFyZ2V0ID0gIkdBVEVTIikKYGBgCgpgYGB7cn0KcGxvdChldmFsdWF0aW9uLCB0YXJnZXQgPSAiR0FURVMiKQpgYGAKCldoZW4gdGhlcmUgaXNu4oCZdCBtdWNoIGRldGVjdGFibGUgaGV0ZXJvZ2VuZWl0eSwgdGhlIHBsb3QgYWJvdmUgY2FuIGVuZCB1cCBiZWluZyBub24tbW9ub3RvbmljLiBUaGlzIGNhbiBtZWFuIHRoYXQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaXMgdG9vIHNtYWxsIGZvciB1cyB0byBiZSBhYmxlIHRvIGRldGVjdCBzdWJncm91cHMgd2l0aCByZWxldmFudCBkaWZmZXJlbmNlcyBpbiB0cmVhdG1lbnQgZWZmZWN0LgoKSW4gb3VyIGNhc2UsIHRoZSBmYXZvcmFibGUgbW9ub3RvbmljaXR5IGlzIGluIHF1ZXN0aW9uLiBGb3IgbW9zdCBlc3RpbWF0b3JzLCB3ZSBjYW4gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgJEhfMDogR0FURVNfNSA9IEdBVEVTXzEkIGF0IHRoZSAqKjEwJSoqIHNpZ25pZmljYW5jZSBsZXZlbCwgYnV0IG5vdCBhdCB0aGUgKio1JSoqIGxldmVsLgoKPGJyPgoKIyMjIFRPQyBjdXJ2ZQoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIHByZXNlbmNlIG9mIGhldGVyb2dlbmVpdHkgYW5kIGhvdyBtdWNoIGJlbmVmaXQgdGhlcmUgaXMgdG8gcHJpb3JpdGl6aW5nIHRyZWF0bWVudCBiYXNlZCBvbiB0aGlzIGhldGVyb2dlbmVpdHkuIEJ5IOKAnGJlbmVmaXTigJ0gb25lIG1lYW5zIHRoZSBleHBlY3RlZCBpbmNyZWFzZSBpbiBvdXRjb21lcyBmcm9tIGdpdmluZyB0cmVhdG1lbnQgdG8gYSBmcmFjdGlvbiBvZiB0aGUgcG9wdWxhdGlvbiB3aXRoIHRoZSAkXGhhdHtcdGF1fSh4KSQgYXMgb3Bwb3NlZCB0byBnaXZpbmcgdHJlYXRtZW50IHRvIGEgcmFuZG9tbHkgc2VsZWN0ZWQgZnJhY3Rpb24gb2YgdGhlIHNhbWUgc2l6ZS4KClRoZSBUT0MgaXMgZGVmaW5lZCBhcyB0aGUgVG90YWwgT3V0Y29tZSBDb250cmFzdCwgZXhwcmVzc2VkIGFzOgoKJCQgVE9DKHU7IFxoYXR7XHRhdX0pID0gRVtZKDEpIC0gWSgwKSB8IEYoXGhhdHtcdGF1fShYKSkgXGdlcSAxIC0gdV0gLSBFW1koMSkgLSBZKDApXSAkJAoKVGhpcyBtZWFzdXJlIGNvbnRyYXN0cyB0aGUgYXZlcmFnZSBlZmZlY3QgaW4gdGhlIHN1Ymdyb3VwIHdpdGggdGhlIHRvcCBgMTAwdSVgIGhpZ2hlc3QgZXN0aW1hdGVkIENvbmRpdGlvbmFsIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdHMgKENBVEVzKSB3aXRoIHRoZSBBdmVyYWdlIFRyZWF0bWVudCBFZmZlY3QgKEFURSkuCgpJZiB0aGUgZXN0aW1hdGVkIENBVEVzIGVmZmVjdGl2ZWx5IGNhcHR1cmUgc3lzdGVtYXRpYyBoZXRlcm9nZW5laXR5LCB3ZSBhbnRpY2lwYXRlOgoKLSAgIHRoZSBUT0MgdG8gYmUgcG9zaXRpdmUgdGhyb3VnaG91dCwgaW5kaWNhdGluZyB0aGF0IHRoZSBhdmVyYWdlIGVmZmVjdCBpbiB0aGUgc3ViZ3JvdXAgd2l0aCBoaWdoIGVzdGltYXRlZCBDQVRFcyBjb25zaXN0ZW50bHkgZXhjZWVkcyB0aGUgb3ZlcmFsbCBhdmVyYWdlIHRyZWF0bWVudCBlZmZlY3QuCgotICAgdGhlIFRPQyB0byBiZSBsYXJnZXN0IGZvciBsb3cgdmFsdWVzIG9mIGB1YCwgc2lnbmlmeWluZyB0aGF0IHRoZSBjb250cmFzdCBpbiBhdmVyYWdlIGVmZmVjdHMgaXMgbW9zdCBwcm9ub3VuY2VkIHdoZW4gZm9jdXNpbmcgb24gdGhlIHN1Ymdyb3VwIHdpdGggdGhlIGhpZ2hlc3QgZXN0aW1hdGVkIENBVEVzLgoKYGBge3J9CnBsb3QoZXZhbHVhdGlvbiwgdGFyZ2V0ID0gIlRPQyIpCmBgYAoKSWYgdGhlcmUgaXMgc2NhcmNlbHkgYW55IGhldGVyb2dlbmVpdHkgaW4gJFx0YXUoWF9pKSQsIHRoaXMgYXJlYSB3aWxsIGJlIHZhbmlzaGluZ2x5IHNtYWxsLiAqKlRoaXMgaXMgcHJhY3RpY2FsbHkgd2hhdCBpcyBvYnNlcnZlZCoqLiBUaGVyZSBhcmUgYmFyZWx5IGFueSBiZW5lZml0cyB0byBzdHJhdGlmeWluZyB0cmVhdG1lbnQuCgpBIG51bGwgZWZmZWN0IGNhbiBiZSBhdHRyaWJ1dGVkIHRvCgoxLiAgTG93IHBvd2VyLCBhcyBwZXJoYXBzIHRoZSBzYW1wbGUgc2l6ZSBpcyBub3QgbGFyZ2UgZW5vdWdoIHRvIGRldGVjdCBIVEVzLAoyLiAgVGhhdCB0aGUgSFRFIGVzdGltYXRvciBkb2VzIG5vdCBkZXRlY3QgdGhlbSwgb3IKMy4gIFRoZSBoZXRlcm9nZW5laXR5IGluIHRoZSB0cmVhdG1lbnQgZWZmZWN0cyBhbG9uZyBvYnNlcnZhYmxlIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIG5lZ2xpZ2libGUuCgpJIHdvdWxkIGxpa2UgYGV2YWx1Q0FURWAgdG8gaW5jbHVkZSBhbiBvcHRpb24gdG8gcmVzdHJpY3QgdGhlIFktYXhpcy4gSW4gdGhpcyBjYXNlLCB0aGUgaW5pdGlhbCBpbnN0YWJpbGl0eSBtYWtlcyB0aGUgYW5hbHlzaXMgY2hhbGxlbmdpbmcuCgpUdXJuaW5nIGEgYmxpbmQgZXllIHRvIHRoZSBUT0MgY3VydmUsIHRoZSByZXN1bHRzIHNlZW0gdG8gYmUgbW9kZXN0bHkgc2lnbmlmaWNhbnQgYW5kIGV2ZW4gcHJvbWlzaW5nLiBIZXRlcm9nZW5laXR5IGlzIHVuY292ZXJlZCwgbGFkaWVzIGFuZCBnZW50bGVtZW4uIFRoaXMgYXNzaWdubWVudCB3YXMgdHJ1bHkgYSBwbGVhc3VyZS4KCiFbKkNhdXNhbCBJbmZlcmVuY2UgbWVtZSBrbm93bGVkZ2Ugd2Fzbid0IGluIHZhaW4qXShhbHdheXNIYXNCZWVuLnBuZyl7d2lkdGg9IjUwMCJ9CgpXYXMgYW55IGhldGVyb2dlbmVpdHkgdW5jb3ZlcmVkLCB0aG91Z2g/IE9yIGlzIHRoaXMgYSBjYXNlIG9mIHNlZWQgaGFja2luZyBhbmQgcHVyZSBsdWNrPwoKPGJyPgoKIyMgOC4gUmVzdWx0cyB2YXJpYWJpbGl0eQoKVG8gZXhwbG9yZSB2YXJpYWJpbGl0eSwgSSBwZXJmb3JtIDEwMCAiTW9udGUgQ2FybG8iIHNpbXVsYXRpb25z4oCUaS5lLiwgSSBydW4gdGhlIGVudGlyZSBhc3NpZ25tZW50IDEwMCB0aW1lcyB1c2luZyBkaWZmZXJlbnQgc2VlZHMuIFRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgaXMgJFxiZXRhXzIkIGluIEJMUC4gSSBjb3VudCBob3cgbWFueSB0aW1lcyBpdHMgY29uZmlkZW5jZSBpbnRlcnZhbHMgY292ZXIgemVybywgZW1wbG95aW5nIGJvdGggV2VpZ2h0ZWQgUmVzaWR1YWwgYW5kIEhvcnZpdHotVGhvbXBzb24gQkxQcyAod2l0aCBkZS1ub2lzaW5nKS4KCkFkZGl0aW9uYWxseSwgSSBjb25kdWN0IHRoZSBzYW1lIG9wZXJhdGlvbiBmb3IgdGhyZWUgZGlmZmVyZW50IHNhbXBsZSBzcGxpdHRpbmcgcnVsZXM6IDUwLzUwLCA3MC8zMCwgYW5kIDMwLzcwLgoKRWFjaCBjb2x1bW4gdGFrZXMgYXJvdW5kIDQwIG1pbnV0ZXMgdG8gcnVuOyB0aGVyZWZvcmUsIHRoZSBjb2RlIGlzIGNvbW1lbnRlZC4KCmBgYHtyfQojIGNvdmVyYWdlID0gbW9udGVfY2FybG8oWCxXLFksaXRlcmF0aW9ucyA9IDEwMCx0cmFpbl9zaGFyZSA9IDAuNSkKIyBjb3ZlcmFnZQpgYGAKCnwgTWV0aG9kICAgICAgICAgICAgICAgfCBUcmFpbi10ZXN0OiA1MC81MCB8IFRyYWluLXRlc3Q6IDcwLzMwIHwgVHJhaW4tdGVzdDogMzAvNzAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBULWxlYXJuZXIgV2VpZ2h0ICAgICB8IDAuMTEgICAgICAgICAgICAgIHwgMC4xNSAgICAgICAgICAgICAgfCAwLjEzICAgICAgICAgICAgICB8CnwgVC1sZWFybmVyIEhUICAgICAgICAgfCAwLjExICAgICAgICAgICAgICB8IDAuMTUgICAgICAgICAgICAgIHwgMC4xNyAgICAgICAgICAgICAgfAp8IFgtbGVhcm5lciBXZWlnaHQgICAgIHwgMC4wOCAgICAgICAgICAgICAgfCAwLjE5ICAgICAgICAgICAgICB8IDAuMjAgICAgICAgICAgICAgIHwKfCBYLWxlYXJuZXIgSFQgICAgICAgICB8IDAuMDkgICAgICAgICAgICAgIHwgMC4yMCAgICAgICAgICAgICAgfCAwLjE5ICAgICAgICAgICAgICB8CnwgQ2F1c2FsIEZvcmVzdCBXZWlnaHQgfCAwLjEzICAgICAgICAgICAgICB8IDAuMTcgICAgICAgICAgICAgIHwgMC4xMyAgICAgICAgICAgICAgfAp8IENhdXNhbCBmb3Jlc3QgSFQgICAgIHwgMC4xNCAgICAgICAgICAgICAgfCAwLjE4ICAgICAgICAgICAgICB8IDAuMTAgICAgICAgICAgICAgIHwKfCBEUi1sZWFybmVyIFdlaWdodCAgICB8IDAuMDMgICAgICAgICAgICAgIHwgMC4xMCAgICAgICAgICAgICAgfCAwLjEzICAgICAgICAgICAgICB8CnwgRFItbGVhcm5lciBIVCAgICAgICAgfCAwLjAzICAgICAgICAgICAgICB8IDAuMTEgICAgICAgICAgICAgIHwgMC4xNSAgICAgICAgICAgICAgfAp8IFItbGVhcm5lciBXZWlnaHQgICAgIHwgMC4wMyAgICAgICAgICAgICAgfCAwLjA3ICAgICAgICAgICAgICB8IDAuMTMgICAgICAgICAgICAgIHwKfCBSLWxlYXJuZXIgSFQgICAgICAgICB8IDAuMDQgICAgICAgICAgICAgIHwgMC4xMCAgICAgICAgICAgICAgfCAwLjE0ICAgICAgICAgICAgICB8Cgo6ICoqVGFibGUgMjoqKiBUaGlyZCBUcmVhdG1lbnQgQXJtIC0gTXVsdGlwbGUgRXN0aW1hdGlvbnMgd2l0aCBWYXJ5aW5nIFNlZWRzIGFuZCBUcmFpbi9UZXN0IFNwbGl0dGluZwoKV2UgYXJlIGFibGUgdG8gZGV0ZWN0IGhldGVyb2dlbmVvdXMgZWZmZWN0cyBvbmx5IHNwb3JhZGljYWxseS4gRWl0aGVyIHRoZSBlZmZlY3RzIGFyZSBob21vZ2VuZW91cyBvciBDQVRFIGVzdGltYXRlcyBhcmUgbm90IHJlbGlhYmxlLgoKPGJyPgoKIyMgOS4gTW92aW5nIHRoZSBnb2FscG9zdAoKVG8gcmVpdGVyYXRlOiB0aGUgbGFzdCBzZWN0aW9uIGhhcyBzaG93biB0aGF0IHRoZXJlIGlzIGVpdGhlciBubyBoZXRlcm9nZW5laXR5IG9yIHRoYXQgd2UgYXJlIHVuYWJsZSB0byBkZXRlY3QgaXQuIERldGVjdGlvbiBpc3N1ZXMgYXJpc2UgZnJvbSBlaXRoZXIgYSB3ZWFrIHNpZ25hbCBvciBpbnN1ZmZpY2llbnQgc2FtcGxlIHNpemUgKGFzIHdlIGtub3csIE1hY2hpbmUgTGVhcm5pbmcgbWV0aG9kcyBhcmUgZGF0YS1odW5ncnkpLgoKSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLCB0byAiYm9vc3QgcG93ZXIsIiBJIHdvdWxkIGFyZ3VlIHRoYXQgdGhlIHRyZWF0bWVudHMgYXJlIHF1aXRlIHNpbWlsYXIuIE9uZSBjYW4gY29tYmluZSB0aGVtIGludG8gIm5vIHRyZWF0bWVudCIgYW5kICJzb21lIHRyZWF0bWVudCwiIHJlc3VsdGluZyBpbiBhbiBpbmNyZWFzZWQgc2FtcGxlIHNpemU6ICoqMTAsNTU5KiogdHJlYXRlZCBhbmQgKiozLDM1NCoqIGluIHRoZSBjb250cm9sIGdyb3VwLgoKVGhlIHBhcnRzIGZvciB0aGUgY29tYmluZWQgdHJlYXRtZW50IGFyZSBjb21tZW50ZWQgdGhyb3VnaG91dCB0aGUgY29kZSB0byBrZWVwIHRoZSBhc3NpZ25tZW50IGNvbmNpc2UgKGFuZCB0byBtYW5hZ2UgdGhlIHJ1bm5pbmcgdGltZSkuIFNpbXVsYXRlZCByZXN1bHRzIGFyZSBwcm92aWRlZCBpbiB0aGUgdGFibGVzLgoKfCBUcmVhdG1lbnQgICAgICAgICAgICAgIHwgRXN0aW1hdGVkIEFURSB8IFN0ZC4gRXJyb3IgfCB0IHZhbHVlIHwgUC12YWx1ZSB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfAp8IFRyZWF0bWVudCAxICAgICAgICAgICAgfCAtMC4wMjc0ICAgICAgIHwgMC4wMzg2ICAgICB8IC0wLjcwICAgfCAwLjQ3OCAgIHwKfCBUcmVhdG1lbnQgMiAgICAgICAgICAgIHwgLTAuMDc4OCAgICAgICB8IDAuMDMyNSAgICAgfCAtMi40MiAgIHwgMC4wMTUzICB8CnwgVHJlYXRtZW50IDMgICAgICAgICAgICB8IC0wLjA2NzEgICAgICAgfCAwLjAzNTMgICAgIHwgLTEuODkgICB8IDAuMDU3NiAgfAp8IFRyZWF0bWVudCA0ICAgICAgICAgICAgfCAtMC4wODU0ICAgICAgIHwgMC4wMzU4ICAgICB8IC0yLjM4ICAgfCAwLjAxNzEgIHwKfCBUcmVhdG1lbnQgNSAgICAgICAgICAgIHwgLTAuMDMzNSAgICAgICB8IDAuMDM1NiAgICAgfCAtMC45NCAgIHwgMC4zNDY2ICB8CnwgVHJlYXRtZW50IDYgICAgICAgICAgICB8IC0wLjA3MTUgICAgICAgfCAwLjAzOTEgICAgIHwgLTEuODIgICB8IDAuMDY3OCAgfAp8ICoqQ29tYmluZWQgdHJlYXRtZW50KiogfCAtMC4wNjIzICAgICAgIHwgMC4wMjQwICAgICB8IC0yLjU4ICAgfCAwLjAwOTYgIHwKCjogKipUYWJsZSAzKio6IEFURSBFc3RpbWF0ZWQgYnkgTmFpdmUgTWVhbiBDb21wYXJpc29uCgpUaGUgaWRlYSBvZiBzaW1pbGFyIHRyZWF0bWVudHMgaXMgZGViYXRhYmxlLCB0byBzYXkgdGhlIGxlYXN0LCBidXQgbGV0J3Mga2VlcCBpdCBmb3IgdGhlIHNha2Ugb2YgdGhlIGFyZ3VtZW50LgoKQXMgYW4gaWxsdXN0cmF0aW9uLCBiZXN0IGxpbmVhciBwcmVkaWN0b3Igd2l0aCBjb21iaW5lZCB0cmVhdG1lbnQgYW5kIGEgNzAlIHRyYWluaW5nIHNhbXBsZSBnaXZlcyB0aGUgZm9sbG93aW5nIHJlc3VsdDoKCiFbXShibGdfYWxsLnBuZyl7d2lkdGg9IjY4NSJ9CgpQZXJmb3JtaW5nIDEwMCAnTW9udGUgQ2FybG8nIHNpbXVsYXRpb25zICh0YWtlcyB0aGUgbW9zdCB0aW1lIGR1ZSB0byBhIDMtZm9sZCBpbmNyZWFzZWQgc2FtcGxlIHNpemUpIGFnYWluLCB3ZSBvYnNlcnZlOgoKYGBge3J9CiMgY292ZXJhZ2UgPSBtb250ZV9jYXJsbyhYLFcsWSxpdGVyYXRpb25zID0gMTAwLHRyYWluX3NoYXJlID0gMC4zKQojIGNvdmVyYWdlCmBgYAoKfCBNZXRob2QgICAgICAgICAgICAgICB8IFRyYWluLXRlc3Q6IDUwLzUwIHwgVHJhaW4tdGVzdDogNzAvMzAgfCBUcmFpbi10ZXN0OiAzMC83MCB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tfAp8IFQtbGVhcm5lciBXZWlnaHQgICAgIHwgMC4wNSAgICAgICAgICAgICAgfCAwLjAzICAgICAgICAgICAgICB8IDAuMDQgICAgICAgICAgICAgIHwKfCBULWxlYXJuZXIgSFQgICAgICAgICB8IDAuMDQgICAgICAgICAgICAgIHwgMC4wMiAgICAgICAgICAgICAgfCAwLjA0ICAgICAgICAgICAgICB8CnwgWC1sZWFybmVyIFdlaWdodCAgICAgfCAwLjA1ICAgICAgICAgICAgICB8IDAuMDIgICAgICAgICAgICAgIHwgMC4wNSAgICAgICAgICAgICAgfAp8IFgtbGVhcm5lciBIVCAgICAgICAgIHwgMC4wNSAgICAgICAgICAgICAgfCAwLjAyICAgICAgICAgICAgICB8IDAuMDYgICAgICAgICAgICAgIHwKfCBDYXVzYWwgRm9yZXN0IFdlaWdodCB8IDAuMDUgICAgICAgICAgICAgIHwgMC4wMiAgICAgICAgICAgICAgfCAwLjA3ICAgICAgICAgICAgICB8CnwgQ2F1c2FsIEZvcmVzdCBIVCAgICAgfCAwLjA0ICAgICAgICAgICAgICB8IDAuMDMgICAgICAgICAgICAgIHwgMC4wNyAgICAgICAgICAgICAgfAp8IERSLWxlYXJuZXIgV2VpZ2h0ICAgIHwgMC4wMyAgICAgICAgICAgICAgfCAwLjAyICAgICAgICAgICAgICB8IDAuMDMgICAgICAgICAgICAgIHwKfCBEUi1sZWFybmVyIEhUICAgICAgICB8IDAuMDIgICAgICAgICAgICAgIHwgMC4wMSAgICAgICAgICAgICAgfCAwLjAzICAgICAgICAgICAgICB8CnwgUi1sZWFybmVyIFdlaWdodCAgICAgfCAwLjAzICAgICAgICAgICAgICB8IDAuMDIgICAgICAgICAgICAgIHwgMC4wMiAgICAgICAgICAgICAgfAp8IFItbGVhcm5lciBIVCAgICAgICAgIHwgMC4wMiAgICAgICAgICAgICAgfCAwLjAyICAgICAgICAgICAgICB8IDAuMDIgICAgICAgICAgICAgIHwKCjogKipUYWJsZSA0OioqIENvbWJpbmVkIFRyZWF0bWVudCAtIE11bHRpcGxlIEVzdGltYXRpb25zIHdpdGggVmFyeWluZyBTZWVkcyBhbmQgVHJhaW4vVGVzdCBTcGxpdHRpbmcKCkhlcmUsIEkgc2hhbGwgYmUgdmVyeSBkZWxpY2F0ZSB3aXRoIG15IGludGVycHJldGF0aW9ucy4gQXMgdGhleSBzYXkgaW4gbW92aWVzLCAiYW55dGhpbmcgeW91IHNheSBjYW4gYW5kIHdpbGwgYmUgdXNlZCBhZ2FpbnN0IHlvdSBpbiBhIGNvdXJ0IG9mIGxhdyIuCgpGaXJzdGx5LCBvbmx5IDEwMCBzaW11bGF0aW9ucyB3ZXJlIGNvbmR1Y3RlZC4gVXNpbmcgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLCB0aGUgbnVtYmVycyBpbiB0aGUgdGFibGUgY2xvc2VseSBhbGlnbiB3aXRoIHRoZSBwcm9iYWJpbGl0eSBvZiBhIHR5cGUgMSBlcnJvciAoZmFsc2UgcG9zaXRpdmUpLiBQZXJzb25hbGx5LCBJIGhhdmUgbW9yZSBjb25maWRlbmNlIGluIHJlamVjdGluZyB0aGUgcHJlc2VuY2Ugb2YgaGV0ZXJvZ2VuZWl0eS4KClNpbXVsdGFuZW91c2x5LCBvbmUgY291bGQgYXJndWUgdGhhdCBub3cgdGhlcmUgaXMgbW9yZSB0cmVhdG1lbnQgaGV0ZXJvZ2VuZWl0eSBhbW9uZyBkaWZmZXJlbnQgdHJlYXRtZW50IGFybXMgdGhhbiB3aXRoaW4gYSBzaW5nbGUgdHJlYXRtZW50IGFybS4gSW5zdGVhZCBvZiBhZGRpbmcgcG93ZXIsIHRoZSBzdGFuZGFyZCBlcnJvcnMgaGF2ZSBpbmNyZWFzZWQsIGxlYWRpbmcgdG8gYSByZWR1Y3Rpb24gaW4gcG93ZXIuCgo8YnI+CgoqKkluIGNvbmNsdXNpb24qKiwgdGhlIGRhdGEgaGF2ZSBiZWVuIHByZXBhcmVkIGFuZCBhbmFseXplZCB1c2luZyBtdWx0aXBsZSBDYXVzYWwgTWFjaGluZSBMZWFybmluZyBtZXRob2RzLiBUaGUgZ29hbCB3YXMgdG8gZGV0ZWN0IHdoZXRoZXIgdHJlYXRtZW50IGVmZmVjdCBoZXRlcm9nZW5laXR5IGlzIHByZXNlbnQuCgpJbiBmYWN0LCB0aGUgQXZlcmFnZSBUcmVhdG1lbnQgKHByb2dyYW0gcGFydGljaXBhdGlvbikgd2FzIG5vdCBzaWduaWZpY2FudCBhdCB0aGUgNSUgbGV2ZWwsIGFuZCB0aGUgdHJlYXRtZW50IHZhcmlhYmxlIGFsc28gc2hvd2VkIGxvdyBwcmVkaWN0aXZlIHBvd2VyIGZvciB0aGUgb3V0Y29tZSB2YXJpYWJsZSAodW5lbXBsb3ltZW50IHNwZWxsKS4KClN1YnNlcXVlbnRseSwgdGhlIGVzdGltYXRlZCBjb25kaXRpb25hbCBhdmVyYWdlIHRyZWF0bWVudCBlZmZlY3RzIChDQVRFKSB3ZXJlIGFuYWx5emVkIHVzaW5nIGJlc3QgbGluZWFyIHByZWRpY3RvciwgR0FURVMsIGFuZCBUT0MgbWV0aG9kcy4gTW9udGUgQ2FybG8gc2ltdWxhdGlvbnMgaW5kaWNhdGUgdGhhdCB0aGVyZSBpcyBubyB0cmVhdG1lbnQgZWZmZWN0IGhldGVyb2dlbmVpdHkgKG9yIHRoYXQgd2Ugd2VyZSBub3QgYWJsZSB0byBkZXRlY3QgaXQpLgoKPGJyPgoKIyMgMTAuIGBldmFsdUNBVEVgIGV4cGVyaWVuY2UKClRvIGJlIGhvbmVzdCwgSSBkbyBub3QgaGF2ZSBlbm91Z2ggZXhwZXJpZW5jZSB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGEgZ29vZCBhbmQgYmFkIFIgcGFja2FnZS4gSSBkaWRuJ3QgZXhwZXJpZW5jZSBhbnkgY29kZSBpc3N1ZXM7IGV2ZXJ5dGhpbmcgd2FzIHdvcmtpbmcgZnJvbSB0aGUgc3RhcnQuIEkgd291bGQgd2lzaCBmb3IgbW9yZSBwbGVhc2luZyB2aXN1YWwgb3V0cHV0IChHQVRFUyBsb29rcyBjb25mdXNpbmcpLCBidXQgdGhpcyBpcyBvbmx5IGEgbWlub3IgcG9pbnQuCg==